Page 1
Created by XMLmind XSL-FO Converter.
Javát tanítok
Bevezetés a programozásba a Turing gépektől a CORBA technológiáig
Bátfai, Norbert, Debreceni Egyetem, Informatikai Kar, Alkalmazott Matematika és Valószínűségszámítás Tanszék <[email protected] >
Juhász, István, Debreceni Egyetem, Informatikai Kar, Információ Technológia Tanszék <[email protected] >
Page 2
Created by XMLmind XSL-FO Converter.
Javát tanítok: Bevezetés a programozásba a Turing gépektől a CORBA technológiáig írta Bátfai, Norbert és Juhász, István
Publication date 2007 Szerzői jog © 2007 Bátfai Norbert, Juhász István
Copyright 2007, Bátfai Norbert, Juhász István. Ez a digitális tartalom Kempelen Farkas Felsőoktatási Digitális Tankönyvtár vagy más által közreadott digitális tartalom a szerzői jogról szóló 1999. évi LXXVI. tv. 33.§ (4) bekezdésében meghatározott oktatási, illetve tudományos
kutatási célra használható fel. A felhasználó a digitális tartalmat képernyőn megjelenítheti, letöltheti, elektronikus adathordozóra vagy papírra másolhatja, adatrögzítő rendszerében tárolhatja. A Kempelen Farkas Felsőoktatási Digitális Tankönyvtár vagy más weblapján
található digitális tartalmak üzletszerű felhasználása tilos, valamint kizárt a digitális tartalom módosítása és átdolgozása, illetve az ilyen
módon keletkezett származékos anyag további felhasználása is.
A jelen digitális tartalom internetes közreadását a Nemzeti Kutatási és Technológiai Hivatal 2006-ban nyújtott támogatása tette lehetővé.
Page 3
i Created by XMLmind XSL-FO Converter.
Ajánlás
Keresztmamának.
Page 4
ii Created by XMLmind XSL-FO Converter.
Tartalom
Bevezetés .......................................................................................................................................... vii 1. A szerzőkről ........................................................................................................................ vii 2. Előszó ................................................................................................................................. viii
2.1. Bíztatás .................................................................................................................... ix 2.1.1. Előismeretek ................................................................................................ ix
2.2. Javasolt használati esetek ........................................................................................ ix 2.2.1. A még teljesen kezdő és csak a Java programozás iránt érdeklődőkről szóló
használati eset ........................................................................................................ x 2.2.2. Csak a Java programozás oktatása iránt érdeklődőkről szóló használati eset x 2.2.3. Szekvenciális feldolgozással kezdő, de ezt a Programozás papíron részben
feladókról szóló használati eset .............................................................................. x 2.3. Köszönet .................................................................................................................. xi 2.4. Figyelmeztetés ......................................................................................................... xi 2.5. Könyvjelzők ............................................................................................................ xi
2.5.1. Szervezési könyvjelzők ............................................................................... xi 2.5.2. Főbb tartalmi könyvjelzők .......................................................................... xi 2.5.3. Technikai könyvjelzők ............................................................................... xii
3. Előzetes a példaprogramokból ............................................................................................ xii 3.1. Egygépes példák .................................................................................................... xiii
3.1.1. A LabirintusVilág példa ............................................................................ xiii 3.1.2. A LabirintusApplet példa .......................................................................... xiii 3.1.3. A LabirintusJáték példa ............................................................................. xiv
3.2. Mobiltelefonos példák ........................................................................................... xiv 3.2.1. LabirintusMIDlet példa ............................................................................. xiv
3.3. Hálózati példák ....................................................................................................... xv 3.3.1. A LabirintusServlet példa .......................................................................... xv 3.3.2. A HálózatiLabirintus példa ....................................................................... xvi 3.3.3. A TávoliLabirintus példa .......................................................................... xvi 3.3.4. A KorbásLabirintus példa ........................................................................ xvii 3.3.5. A ElosztottLabirintus példa ...................................................................... xvii
3.4. További példák .................................................................................................... xviii 3.4.1. A további önálló példák könyvjelzői ......................................................... xix 3.4.2. A példák jellege ......................................................................................... xx 3.4.3. Összefoglalás ............................................................................................. xx 3.4.4. Platform ..................................................................................................... xxi 3.4.5. A példaprogramok szerkezete, kipróbálása és a kézikönyv jelölései ........ xxi
4. Látványos Ars Poetica ....................................................................................................... xxv I. Programozás papíron ....................................................................................................................... 1
1. A programozásról .................................................................................................................. 2 1. A programozás filozófiája ........................................................................................... 2
1.1. A programozás alapjai ..................................................................................... 3 1.1.1. Algoritmikus kérdések ........................................................................ 3 1.1.2. A programozás evolúciója ................................................................ 49 1.1.3. A programozás egy filogenetikai törzsfája ....................................... 49 1.1.4. Bepillantás napjaink gyakorlatába .................................................... 52 1.1.5. Néhány látomás: a jövő programozása ............................................. 52
1.2. Az OO világ, a programozó szűkebb hazája ................................................. 59 1.2.1. A Java platform ................................................................................ 61 1.2.2. Objektumok mindenütt: a CORBA OO világ ................................... 73
1.3. Az internet, a programozó tágabb hazája ...................................................... 76 1.3.1. TCP/IP .............................................................................................. 76 1.3.2. A kliens-szerver modell .................................................................... 77 1.3.3. Bepillantás az alkalmazási rétegbe: a HTTP protokoll ..................... 77
2. Saját világok teremtése és Java alapok ................................................................................ 81 1. A Java világa ............................................................................................................. 81
1.1. A Java nyelv .................................................................................................. 81
Page 5
Javát tanítok
iii Created by XMLmind XSL-FO Converter.
1.1.1. .......................................................................................................... 83 1.1.2. Osztályok és objektumok .................................................................. 84 1.1.3. A programozás világnyelve a Java ................................................... 86 1.1.4. Vissza az OO-hoz ........................................................................... 104 1.1.5. Mi történik a metódusokban? ......................................................... 117 1.1.6. Eseménykezelés .............................................................................. 123 1.1.7. Kivételkezelés ................................................................................. 126 1.1.8. Párhuzamos végrehajtás ................................................................. 130 1.1.9. Interfészek ...................................................................................... 132 1.1.10. Csomagok ..................................................................................... 132
1.2. A Java nyelv használata .............................................................................. 132 1.2.1. Bepillantás a GUI programozásba .................................................. 132 1.2.2. Bepillantás a hálózati programozásba ............................................. 135 1.2.3. Esettanulmány: egy chat program ................................................... 136
II. Programozás gépen .................................................................................................................... 149 3. Java esettanulmányok ........................................................................................................ 150
1. Labirintus esettanulmányok Java nyelven ............................................................... 150 1.1. A tiszta OO labirintus - Labirintus Világ .................................................... 150
1.1.1. A labirintus API felélesztése .......................................................... 150 1.1.2. A LabirintusVilág osztály ............................................................... 167 1.1.3. A labirintus API és a LabirintusVilág a NetBeans IDE környezetben 172
1.2. Java a játékokban: egy teljes képernyős példa - Labirintus Játék ............... 175 1.2.1. A LabirintusJáték osztály ............................................................... 175 1.2.2. A teljes képernyős labirintus fordítása, futtatása ............................ 181
1.3. Java a böngészőkben: Applet objektumok - Labirintus Applet ................... 182 1.3.1. A LabirintusApplet osztály ............................................................. 182 1.3.2. A labirintus applet fordítása, futtatása ............................................ 186
1.4. Java a mobiltelefonokban: MIDlet objektumok - Labirintus MIDlet .......... 188 1.4.1. A LabirintusMIDlet osztály ............................................................ 192 1.4.2. A LabirintusVaszon osztály ............................................................ 194
1.5. Java a webszerverekben: Servlet objektumok - Labirintus Servlet ............. 197 1.5.1. A LabirintusServlet osztály ............................................................ 201
1.6. Java a hálózaton .......................................................................................... 204 1.6.1. TCP/IP - Hálózati Labirintus .......................................................... 204 1.6.2. Java RMI - Távoli Labirintus ........................................................ 213 1.6.3. CORBA - Korbás Labirintus .......................................................... 219
1.7. Elosztott objektumok - Elosztott labirintus ................................................. 226 1.7.1. Az elosztott labirintus API felélesztése .......................................... 227 1.7.2. Az Elosztott labirintus fordítása és futtatása ................................... 244
4. Példaprogramok ................................................................................................................ 249 1. A csomagok szervezése ........................................................................................... 249
1.1. A példaprogramok forrásainak letöltése ...................................................... 249 1.2. javattanitok.labirintus csomag ..................................................................... 249 1.3. javattanitok.elosztott csomag ...................................................................... 250 1.4. javattanitok csomag ..................................................................................... 250
1.4.1. A LabirintusAlkalmazás osztály ..................................................... 251 5. A példák kipróbálása ......................................................................................................... 257
1. A Java telepítése gépünkre ...................................................................................... 257 1.1. A Java SE, Java ME fejlesztői csomag letöltése és beállítása ..................... 257
1.1.1. Java SE Linux környezetben .......................................................... 257 1.1.2. Java SE Windows környezetben ..................................................... 258 1.1.3. A Java dokumentáció, azaz az API doksi letöltése ......................... 258 1.1.4. Java ME .......................................................................................... 259 1.1.5. A NetBeans integrált fejlesztői környezet letöltése és használata .. 259 1.1.6. A NetBeans IDE 5.5 ....................................................................... 260
2. A példaprogramok futtatásáról általában ................................................................. 260 A. Java mellékletek ............................................................................................................... 262
1. A Java verziók újdonságai a tigristől a delfinig ....................................................... 262 1.1. A tigris ........................................................................................................ 262 1.2. A Musztáng ................................................................................................. 264
Page 6
Javát tanítok
iv Created by XMLmind XSL-FO Converter.
1.3. A delfin ....................................................................................................... 268 2. Az első Java tapasztalatok ....................................................................................... 268 3. Egyszerű összehasonlítások ..................................................................................... 270
3.1. Egyszerű összehasonlítások a sebesség kérdésében .................................... 270 3.1.1. A PiBBPBench Java osztály ........................................................... 272 3.1.2. A pi_bbp_bench forrás ................................................................... 274 3.1.3. A PiBBPBench C Sharp osztály ..................................................... 276
3.2. A Java és a C Sharp nyelvi szintű összehasonlítása .................................... 278 3.2.1. Az alapvető nyelvi elemek összehasonlítása .................................. 283
B. Számítási mellékletek ....................................................................................................... 287 1. Biológiai témájú programok .................................................................................... 287
1.1. Genomi, aminosav vagy akár tetszőleges szekvenciák összehasonlítása .... 287 1.1.1. A Pontmátrix osztály ...................................................................... 292 1.1.2. A Swinges felület építésének alapszabálya .................................... 298
1.2. Sejtautomata szimuláció programja ............................................................ 299 1.3. Orch OR demonstrációk .............................................................................. 312
1.3.1. Hexagonális rács ............................................................................. 312 2. Matematikai témájú programok ............................................................................... 320
2.1. Galton deszka kísérlet programja ................................................................ 320 2.2. Mandelbrot halmaz programja .................................................................... 325 2.3. Mandelbrot halmaz nagyító programja ....................................................... 333 2.4. Mandelbrot halmaz pontjait grafikusan iteráló program ............................. 348 2.5. A Mandelbrot halmazzal kapcsolatos osztályaink összefoglalása ............... 351
2.5.1. A MandelbrotHalmaz osztály ......................................................... 352 2.5.2. A MandelbrotHalmazNagyító osztály ............................................ 356 2.5.3. A MandelbrotIterációk osztály ....................................................... 359
2.6. A Pi jegyeinek nyomában ........................................................................... 361 Irodalomjegyzék ............................................................................................................................. 365
Page 7
v Created by XMLmind XSL-FO Converter.
A táblázatok listája
1.1. A bonyolultságmérő programok összefoglalása. ....................................................................... 14 1.2. A bonyolultságmérő programok és bemenetük együttes összefoglalása. .................................. 15 1.3. A 01-et ismétlő bináris sorozat kezdőrészeinek vizsgálata. ....................................................... 36 1.4. A félig véletlen bináris sorozat kezdőrészeinek vizsgálata. ....................................................... 39 1.5. Mikrotubulus sejtautomata szimuláció. ..................................................................................... 56 1.6. Java és C egyszerű sebesség összehasonlítása ........................................................................... 65 2.1. A Java primitív típusai ............................................................................................................. 117 A.1. Java, gcj és C egyszerű sebesség összehasonlítása ................................................................. 272 A.2. Java és C# egyszerű sebesség összehasonlítása ...................................................................... 272 B.1. Az R. W. Gosper-féle sikló ágyú élőlény bemutatása ............................................................. 306
Page 8
vi Created by XMLmind XSL-FO Converter.
A példák listája
1.1. Első sorozat: 1000 nulla ............................................................................................................. 10 1.2. Első sorozat, máshogy: 1000 nulla ............................................................................................ 11 1.3. Második sorozat: 01010101 ... ................................................................................................... 11 1.4. Harmadik sorozat: 00000000001 ... 10000000000 .................................................................... 12 1.5. Negyedik sorozat: egyre több 1 jegy két nulla között ................................................................ 13 1.6. Ötödik sorozat: pszeudo véletlen ............................................................................................... 13 1.7. Bármely sorozat: egy másoló program ...................................................................................... 14 1.8. A Chaitin-Kolmogorov bonyolultság nem kiszámítható! .......................................................... 17 1.9. A Chaitin-Kolmogorov bonyolultság gyakorlati alkalmazásai .................................................. 18 1.10. Átváltás binárisba .................................................................................................................... 19 1.11. Bitműveletek, kezdjük egy bináris dumppal ............................................................................ 21 1.12. Titkosítás kizáró vaggyal ......................................................................................................... 22 1.13. Kizáró vagyos titkosítás grafikusan ......................................................................................... 24 1.14. Számrendszer átváltások .......................................................................................................... 24 1.15. A TörtÁtváltó osztály kiegészítése .......................................................................................... 25 1.16. Turing gépek kódolása ............................................................................................................. 26 1.17. Kongruencia generátorok ......................................................................................................... 26 1.18. Galton deszka kísérlet .............................................................................................................. 28 1.19. Hisztogram feladat ................................................................................................................... 31 1.20. Fej vagy írás varázslat ............................................................................................................. 34 1.21. Egy szabályos sorozat .............................................................................................................. 35 1.22. Véletlenszám generátorok ....................................................................................................... 37 1.23. Egy másik szabályos sorozat ................................................................................................... 38 1.24. Egy nem véletlen, de bonyolultabb sorozat ............................................................................. 38 1.25. A Chaitin-féle Omega konstans ismeretében meg tudnánk oldani a megállási problémát! ..... 42 1.26. A Pi közelítése ......................................................................................................................... 43 1.27. A Ramanujan és a Chudnovsky közelítő összegek formulái ................................................... 45 1.28. Az Ar klasszikus elsőrendű matematikai logikai nyelv ........................................................... 47 1.29. Saját terület formalizálása ........................................................................................................ 48 1.30. Mikrotubulus sejtautomata szimuláció .................................................................................... 56 1.31. Java disassembler .................................................................................................................... 63 1.32. Port szkennelő példa ................................................................................................................ 79 1.33. Jól ismert portok feladat .......................................................................................................... 80 2.1. Saját labirintusunk elképzelése .................................................................................................. 83 2.2. A RuntimeException típusú hibák kezeléséről 2. .................................................................... 130 A.1. Írjunk az OsztályNév osztályunkhoz egy példánymetódust, ami visszaadja az osztály String tagját!
......................................................................................................................................................... 285 B.1. Pontmátrix osztály kiegészítése .............................................................................................. 296 B.2. További kísérletek a Pontmátrix osztályunkkal ...................................................................... 297 B.3. A Galton deszka kísérlet programjának kiegészítései ............................................................. 324 B.4. A Mandelbrot halmaz nagyító programjának kiegészítései .................................................... 346
Page 9
vii Created by XMLmind XSL-FO Converter.
Bevezetés
„Ha a kéket veszed be... a játéknak vége. Felébredsz az ágyadban, azt hiszel, amit hinni akarsz. De ha a pirosat:
maradsz Csodaországban. És én megmutatom, milyen mély a nyúl ürege.” — MÁTRIX
Ebben a bevezető részben
• röviden bemutatjuk a szerzőpárost
• olvashatjuk a szerzők előszavát
• a könyv néhány javasolt használati esetét
• a szereplő esettanulmányok koncepcionális szintű bemutatását
• megtaláljuk itt a legfontosabb könyvjelzőket
• megismerhetjük a könyvben használt jelöléseket
1. A szerzőkről
Bátfai Norbert 1996-ban szerzett programozó matematikusi, majd 1998-ban kitüntetéses programtervező
matematikusi oklevelet a Debreceni Egyetemen, többek között éppen Juhász István tanítványaként. 1998-ban
megnyerte a Java Szövetség Java Programozási Versenyét.
Bátfai Erikával közös mobil-információtechnológiai cége, az Eurosmobil, második helyezést ért el 2004-ben a
Motorola JavaJáték Versenyén, ugyancsak az Eurosmobil 2004-ben a Sun és a Nokia közös Mobil Java
Fejlesztői Versenyén a Ha hívsz, támadok! - (H.A.H) hálózati (Java EE szerver, Java ME kliens) játéksorozattal
első díjat nyert. 2005-ben az Eurosmobil képviseletében Bátfai Erikával A Java mobiljáték-fejlesztés elmélete és
gyakorlata és a kék (JSR 82) játékok címmel előadott a Java 10. születésnapja alkalmával megrendezett 5. Sun
Java Fejlesztői Napon.
Társszerzője a Fantasztikus programozás (Jávácska Vortál, Jávácska Barátai) című ismeretterjesztő
kalandregény sorozatnak.
Jelenleg a Debreceni Egyetem Informatikai Kara Alkalmazott Matematika és Valószínűségszámítás
Tanszékének munkatársa. Oktatási tapasztalata az alábbi tárgyak gyakorlatain alapul: Java esettanulmányok,
J2SE hálózatok, Java appletek, CORBA, Programozás, Hálózatok, Formális nyelvek és automaták,
Algoritmuselmélet, Bevezetés az informatikába, Operációs rendszerek, Alkalmazások fejlesztése WWW-re,
Objektumorientált programozás a középiskolában, Mobil programozás.
Szerzője a Programozó Páternoszter programozás jegyzetnek.
Page 10
Bevezetés
viii Created by XMLmind XSL-FO Converter.
Juhász István 1975-ben végzett a Kossuth Lajos Tudományegyetem matematika-fizika szakán, jelenleg a
Debreceni Egyetem Informatikai Karának oktatója. 1982-ben egyetemi doktori címet szerzett. Kutatómunkáját a
matematikai statisztika területén végezte.
1974 óta vesz részt az egyetemi oktatásban. Elsősorban programtervező informatikusokat, programozó
matematikusokat, programtervező matematikusokat, informatika tanárokat, informatikus könyvtárosokat,
matematikusokat, és matematika tanárokat tanított illetve tanít. Főbb oktatási területei: programozás,
adatszerkezetek, adatmodellek, adatbázis-kezelő rendszerek, rendszerfejlesztési technológiák
Az egyetemen a kreditrendszerű képzés koncepciójának és a programtervező informatikus BSC szak illetve a
programtervező informatikus MSC információs rendszerek szakirány egyik kidolgozója volt.
Rendszeresen foglalkozik TDK-s hallgatókkal. Az elmúlt években két első díjas és több helyezést elért dolgozat
született a vezetésével. Tagja az OTDK Informatika szekció Szakmai Bizottságának.
Az utóbbi években társszerzőkkel több könyvet írt és fordított, illetve lektorált és szerkesztett, elsősorban az
információ technológia, az adatbázis-kezelés és a web területén. Írt két elektronikus jegyzetet is.
Jelenlegi kutatási területei: objektumorientált és azon túli paradigmák, objektumorientált adatmodellek,
komponens technológia, az informatika oktatásának didaktikai problémái.
2. Előszó
A programozásra gondolva olykor valami misztikus érzés járja át az embert, ami további hajtóerőt és lelkesedést
kölcsönöz. A sikeres oktatás elsődleges célja ennek átadása, ebből fejlődhet majd ki minden más, ami csak kell:
az ipar számára a professzionális programozó, a tudománynak és az emberiségnek a komplex problémákkal
megküzdeni tudó algoritmuselmélész, az egyénnek és a társadalomnak a boldog és tetterős polgár. Reményeink
szerint ennek az érzésnek az átadásához ezzel a digitális szakkönyvvel mi is hozzá tudunk járulni, miközben
általában foglalkozunk a programozással, annak elméletével és filozófiájával, majd részletesen és gyakorlatiasan
az objektumorientált paradigmával, és ezen belül a Java programozással.
Minden OO programhoz kapcsolható egy sajátos világ, egy mikrokozmosz. A kisebb, egyszerűbb programok
esetén ez a mikrokozmosz általában nem külön létező, hanem csupán egy már létező világ parányi, kiragadott
része. A nagyobb, bonyolultabb programok esetén maga a program, azaz a programozó építheti fel ezt a
mikrokozmoszt. Példaként tekintsünk egy labirintus játék programot, amiben a világ maga a labirintus,
kincsestől, szörnyestől, hősöstől, a szokásos történettel: a hős bolyong, a szörnyek próbálják megenni, a kincs
várja, hogy rábukkanjanak. Az OO programozás ereje abban rejlik, hogy a programozó a fejlesztés során rá van
utalva, hogy saját világokat építsen, saját világokat teremtsen! Építőelemei a választott OO platform API
interfészének osztályai, habarcsa pedig maga a választott OO nyelv.
Ebben a kézikönyvben a Java nyelvvel egy labirintus játék világának felépítése során ismerkedünk meg, ami
során ezt az egyszerű, példa labirintus játékot elkészítjük a weboldalon elhelyezhető appletként, mobiltelefonos
és teljes képernyős PC-s, illetve hálózati: TCP/IP, Java Servlet, Java RMI és CORBA változatban is. De ezeken
a labirintus témájú esettanulmányokon túl biológiai, matematikai és informatikai programozási példákkal is
megismerkedhet majd a kedves Olvasó.
A könyvet elsősorban tanári kézikönyvként ajánljuk középiskolai informatikatanároknak, de kiegészítő
irodalomként hasznos olvasmánynak tartjuk informatikus tanárjelöltek, diákok és általában a programozni
tanulni vágyók számára egyaránt.
Page 11
Bevezetés
ix Created by XMLmind XSL-FO Converter.
Bátfai Norbert
2.1. Bíztatás
A kézikönyvet itt-ott felütve, a gyakorlati és az elméleti érdeklődésű Olvasót is elbizonytalaníthatja egy-egy
dolog azt illetően, hogy lelkesen vesse bele magát az olvasásába.
A gyakorlati érdeklődésűeknek meglepő lehet, hogy a programozás alapjairól szóló részekben elméleti
konstrukciókkal, például Turing gépekkel találkoznak, amik esetleg korábbi tanulmányaik során nem váltak
kedvenceikké, vagy az is könnyen meglehet, hogy egyáltalán nem is ismerik ezeket a gépeket. Nekik mégis azt
tanácsoljuk, ne hagyják ki ezeket a fejezeteket, mert nem öncélúan, hanem valódi élmények nyújtásának céljával
tárgyaljuk - ráadásul korántsem kimért matematikai, hanem gyakorlati és ismeretterjesztő szinten - ezeket a
képzeletbeli gépeket. Olyan mentális élmények lesznek ezek, amiket némi erőfeszítéssel bárki befogadhat, de a
programozók, az informatikusok különösen könnyen, mivel ezek az tézisek az ő gondolkodási paradigmáik
szerint épülnek fel, mert alapfogalmuk a számítógépes program. S mi éppen e gyakorlati megközelítéssel
tárgyaljuk ezeket az emberi gondolkodást - programozói filozofálást - avagy a matematikai vénát messzire
elvezető alapfogalmakat.
A másik, ami az elméleti érdeklődésűeket és a kevésbé gyakorlottakat lepheti meg, hogy a valódi programozási
részekben olyan példákkal találkozhatnak, melyek leginkább a haladó és nem a bevezető kurzusok témái. Ilyen
témák például a mobil, a hálózati vagy az elosztott programozás. De itt is megnyugtathatjuk a kedves Olvasót,
hogy bevezető szinten tárgyaljuk a példákat, ahol nem a finomságokat akarjuk elemezni, hanem a lényeget
megmutatni. Ennek megfelelően persze megadunk további szakirodalmi hivatkozásokat, ahol az itt érintett,
haladóbb témák részleteiben is elmerülhetnek az érdeklődő Olvasók.
2.1.1. Előismeretek
A kézikönyv gyakorlatias szemlélete a Java programozási részekben úgy jelenik meg, hogy inkább a szerzők
adott Java akcentusát tükrözi, semmint a Java nyelvi utasítások, konstrukciók általános kimerítő tárgyalását.
Annak a kedves Olvasónak, aki ezt hiányként éli meg, a kézikönyv feldolgozása mellé bevezető könyvként a
[JAVA START] vagy a [JAVA KÖNYV] első kötetének elejét ajánljuk a figyelmébe.
Természetesen a könyv feltételezi a számítógépes alapismeretek meglétét, némi programozási ismeretet, az
internet és a web használatát, de elsősorban a motivációra, az alkotni vágyásra és sok-sok gyakorlásra épít.
2.2. Javasolt használati esetek
Az alábbi feldolgozási módokat javasoljuk, ha a kedves Olvasó
i. Középiskolai informatikatanár
Ha a kedves Olvasó már rendelkezik némi Java programozási tapasztalattal, akkor a kézikönyv folyamatos
olvasása nem okozhat gondot.
Ha az Olvasó még nem rendelkezik Java programozási tapasztalatokkal, s ez elbizonytalanítja, akkor e
bevezető részek elolvasása után Az első Java tapasztalatok című melléklet gyors feldolgozását javasoljuk. E
a pontnak nem küldetése a Java programozás bevezetése, hiszen ezt a célt maga az egész kézikönyv célozta
meg. Célja viszont néhány egyszerű, de nem pofonegyszerű, példa végigvitelével az Olvasó eme említett
bizonytalanság érzésének eloszlatása. Ha viszont a bizonytalanság érzése ezen az ágon mégsem jelenik meg,
akkor itt is bátran kezdje a folyamatos olvasást és kövesse a menet közben szereplő feldolgozási utasításokat.
ii. Informatikai jellegű szak felsőoktatásbeli hallgatója
Ha a kedves Olvasó már rendelkezik némi Java programozási tapasztalattal, akkor a folyamatos olvasás itt
sem okozhat gondot. Az esetlegesen mégis felmerülő megértésbeli problémákat a hasonló érdeklődésű
csoporttársakkal való átbeszélés bizonyára sikeresen orvosolja. Ezen az ágon az elméleti, a Programozás
papíron című rész átolvasása után rögtön a Programozás gépen című rész esettanulmányainak feldolgozását
javasolhatjuk. Ezen belül azt a tetszőleges témát, amely leginkább felkeltette az Olvasó érdeklődését.
Ha az Olvasó még nem rendelkezik Java programozási tapasztalatokkal, akkor a kézikönyv szekvenciális
feldolgozásával párhuzamosan érdemes lehet egy bevezető Java kurzus felvétele az Olvasó oktatási
Page 12
Bevezetés
x Created by XMLmind XSL-FO Converter.
tanintézményében. A Programozás papíron című rész elméleti meggondolásait pedig a Matematikai logika,
Formális nyelvek és automaták vagy leginkább az Algoritmuselmélet című, tartalmú kurzusok elvégzésével
mélyítheti el.
iii. Informatikai jellegű tárgy középiskolai tanulója
Ha a kedves Olvasó már rendelkezik némi Java programozási tapasztalattal, de a folyamatos olvasás során
mégis valamilyen megértésbeli gondja támad, akkor barátaival való megbeszélése után érdemes azt felvetnie
informatikatanárának, szakkörvezetőjének, akitől bizonyára megkapja a megfelelő útbaigazítást.
Ha az Olvasó még nem rendelkezik Java programozási tapasztalatokkal, akkor a feldolgozást A Java világa
című, a Java nyelvi programozást részletesen bevezető résszel javasoljuk kezdeni.
2.2.1. A még teljesen kezdő és csak a Java programozás iránt érdeklődőkről szóló használati eset
Ebben az esetben az alább belinkelt feldolgozási sorrendet javasoljuk pontosan követni a kedves Olvasónak.
1. Előzetes a példaprogramokból
2. Az OO világ, a programozó szűkebb hazája
3. A Java platform
4. Az első Java osztály lefordítása
5. Történet és oktatás
6. A Java OO világ
7. Java SE OO világ
8. Saját világok teremtése és Java alapok
9. A Java világa részletes feldolgozása
10. A választott Java esettanulmányok feldolgozása
2.2.2. Csak a Java programozás oktatása iránt érdeklődőkről szóló használati eset
Ebben az esetben feltehetjük, hogy a kedves Olvasó már nem - az előző pontnak megfelelő - teljesen kezdő.
Ekkor az alább belinkelt feldolgozási sorrendet javasoljuk követni.
1. Előzetes a példaprogramokból
2. Saját világok teremtése és Java alapok
3. A Java világa részletes feldolgozása
4. A választott Java esettanulmányok feldolgozása
2.2.3. Szekvenciális feldolgozással kezdő, de ezt a Programozás papíron részben feladókról szóló használati eset
Ebben az esetben feltehetőleg érdemben olvasni szerette volna az éppen szereplő, de csupán megelőlegezett
Java forrásokat is a kedves Olvasó. Ha továbbra is ragaszkodik ehhez a folyamatosan és mélyen megértő
feldolgozási módhoz, akkor ugorjon az alább belinkelt részre és ennek feldolgozása után már könnyen legyőzi a
most feltorlódott akadályokat.
1. Saját világok teremtése és Java alapok
Page 13
Bevezetés
xi Created by XMLmind XSL-FO Converter.
2.3. Köszönet
Ez a kézikönyv a Nemzeti Kutatási és Technológiai Hivatal, DIGITÁLIS SZAKKÖNYV, DIGIT 2005 pályázat
keretében készült el, ezért köszönetünket elsődlegesen ennek a támogatásnak kell címeznünk. Köszönjük
továbbá a Debreceni Egyetem Informatikai Kar Információ Technológia Tanszékének és Alkalmazott
Matematika és Valószínűségszámítás Tanszékének, mint a szerzők munkahelyeinek, hogy otthont adtak a
pályázat teljesítésének és ezzel egyetemben lehetővé tették az egyetemi hálózat és gépek használatát. Továbbá
köszönjük az EUROSMOBIL-nak, hogy több szereplő, de különösen a mobiltelefonos és a Linuxos Sun
W1100Z Workstation munkaállomáson futtatott példák kipróbálásához és a kézikönyv kifejlesztéséhez a
megfelelő infrastruktúrát a rendelkezésünkre bocsátotta. Végül itt is megköszönjük a lektorok: Korotij Ágnes és
Vágner Anikó munkáját.
2.4. Figyelmeztetés
Nincs felelősségvállalás
A szerzők a példák elkészítésekor a legjobb tudásuk szerint jártak el, de előfordulhatnak, sőt bizonyára
vannak is hibák a programokban, a könyvben. A programok és általában a könyv bármely
felhasználásával kapcsolatba hozható esetleges károkért a szerzők semmilyen felelősséget nem
vállalnak.
Még arra hívjuk fel az Olvasó figyelmét, hogy a szereplő példaprogramok oktatási céllal készültek,
ezért tipikusan valami olyan tulajdonságot mutatnak, amit velük kapcsolatban a könyvben kiemelünk,
feldolgozunk. Például a labirintus elosztott változata azt akarja megmutatni, hogy minden szereplő
objektum külön számítógépen van. De ennél nem többet, ennek megfelelően nem foglalkozik például -
az egyébként természetesen felmerülő - hálózati terheléssel, szinkronizációs problémákkal,
hibakezeléssel, magas szereplőszám esetén a működőképességgel kapcsolatos kérdésekkel.
2.5. Könyvjelzők
2.5.1. Szervezési könyvjelzők
I. A példaprogramok rövid bemutatása
II. A példaprogramok szervezése és forrásszövegek
2.5.2. Főbb tartalmi könyvjelzők
A kézikönyv főbb tartalmi elemeit az alábbi felsorolásba linkeltük be. Az esettanulmányok példáitól eltérő
további gyakorlati példákat egy későbbi pont alatt bontjuk ki bővebben.
I. A programozás és a programozó gondolkodásának alapjai
i. Turing gép
ii. Megállási (végtelen ciklus) probléma
iii. Chaitin-Kolmogorov-Solomonoff bonyolultság
iv. Chaitin gépek és az Omega
II. A programozás filogenetikája
III. A jövő programozása
i. A Penrose-Hameroff Orch OR tudatmodell
IV. Bevezetés a Java programozásba
i. OO tervezés és egyben Java alapok
Page 14
Bevezetés
xii Created by XMLmind XSL-FO Converter.
ii. Java programfejlesztés
iii. Java újdonságok a Tigristől a Musztángon át a Delfinig
V. Labirintusos Java esettanulmányok
i. „Tiszta” OO implementálás
ii. Java Applet
iii. Teljes képernyő - Full Screen Exclusive Mode
iv. Java ME - mobil programozás
v. Szerveroldali Java
a. java.net csomag
b. Java Servlet
c. Java RMI
d. Java IDL
vi. CORBA - elosztott programozás heterogén OO környezetben
VI. További programozási példák
i. Biológiai témájú programok
a. Genomok, fehérjék összehasonlítása
ii. Matematikai témájú programok
a. Fraktálok nagyítása
b. Sejtautomaták
c. A Pi hexadecimális jegyei
d. Galton deszkás kísérletek
2.5.3. Technikai könyvjelzők
I. A Java SE, ME telepítése
II. A jegyzet példaprogramjainak fordítása, futtatása
III. A jegyzet példaprogramjai forrásainak letöltése
3. Előzetes a példaprogramokból
„Minden számítógép-pedagógus tudja a világon, hogy játékokkal kell kezdeni.” —Marx György
Ebben a bevezető részben megtudjuk, milyen példákkal ismerkedhetünk meg a kézikönyv forgatása során. Az
anyagban szereplő számos példára, kódrészletre igaz, hogy részei a jegyzethez készített, a következőkben
felsorolt különböző labirintus játékoknak. Az Olvasó alapértelmezésben, azaz a folyamatos, szekvenciális
olvasás során ugyanebben a sorrendben fog találkozni ezekkel a programokkal. Javaslatunk, hogy - a példák
egymásra épülése és a könyv bevezető jellege miatt - ezen sorrendtől ne térjen el a feldolgozás során, hacsak
más iránymutatást nem kapott a korábbi Javasolt használati esetek című pontban.
Page 15
Bevezetés
xiii Created by XMLmind XSL-FO Converter.
Megjegyzés
Ebbe a szekvenciális olvasásba az is beletartozik, hogy az Olvasó tudásszintjétől függően arra kaphat
utasítást, hogy egy részt éppen hagyjon ki és egy másik rész feldolgozása után térjen ide vissza vagy
éppen ugorjon egy másik, valamit mélyebben kibontó részre.
3.1. Egygépes példák
Az egygépes példák, mint nevük is mutatja, a kézikönyvet egyetlen gép mellett ülve feldolgozó Olvasóknak
nyújtják a programozás élményét. De ebbe a csokorba szedtük a labirintus appletes esettanulmányt is. Nem
csupán azért, mert egyszerű alkalmazásként is futtatható, sokkal inkább azért, mert nem használ aktív
hálózatkezelést; abban az értelemben, hogy jelen programozói nézőpontunkból, egy honlapról történő applet
letöltést passzív hálózatkezelésnek tekintünk.
3.1.1. A LabirintusVilág példa
Kezdetben elkészítjük labirintusunk világát: magát a labirintust, kincsestől, szörnyestől, hősöstől, a szokásos
történettel: miszerint a hős bolyong, a szörnyek próbálják megenni, a kincs várja, hogy rábukkanjanak. A
kézikönyv fő feladata megmutatni, hogyan meséljük el, mutassuk be ezt az elképzelt labirintus világot Java
nyelven. Ha ezzel az elbeszéléssel, leírással elkészültünk, akkor ebben a példában, ebben a programban - avagy
Java nyelven majd azt mondjuk: ebben a LabirintusVilág nevű osztályban - keltjük életre ezt az elbeszélést.
Ennek megfelelően ez az osztály nem fogja csillogó grafikus felülettel felruházni elbeszélésünket, hanem
csupán egy karakteres megjelenést ad majd, hogy megmutassa, hogyan kel életre ez a most teremtett tiszta OO
mikrovilágunk: a labirintus.
A példaprogramokban a téma, a labirintus mikrovilágának életre keltése közös, de a megcélzott platformok és a
létrehozott labirintusok funkcionális szintje már markánsan különböző. Ez utóbbira példa, hogy a jelen program
a labirintus világának valóságát önálló idővel ruházza fel, azaz a labirintus világának valóságában az idő a
játékostól, azaz a hős lépéseitől függetlenül telik, szemben például majd a következő példával, ahol a labirintus
világának valóságában mindig csak akkor üt egyet az óra, ha a játékos, azaz a hős lép egyet. A platformok
különbözősége kapcsán arra gondoljunk, hogy a példák között van Java SE, Java ME, Java EE platformra és
CORBA architektúrára készített program is. A Java SE a Java alapkiadása, egyelőre elég annyi róla, hogy abban
az esetben, ha nem tudjuk, hogy az aktuálisan írt Java programunkat mégis éppen milyen platformra kéne
felkészítenünk, akkor valószínűleg az alapkiadásra, azaz a Java SE kiadásra, a Java sztenderd kiadásra van
szükségünk.
3.1.2. A LabirintusApplet példa
Page 16
Bevezetés
xiv Created by XMLmind XSL-FO Converter.
Ez az osztály a labirintus mikrovilágának egy appletbeli életre keltésére ad példát. Az Applet objektumok azok
a Java SE platformbeli objektumok, akik képesek a böngészőkben létezni, s mint ilyenek megjelenni az
internetes weblapokon. Ezzel, mintegy varázsütésre teremtett labirintus mikrovilágunk már nemcsak a mi PC-
nken létezhet, hanem bármely olyan internetes gép böngészőjében, amelyről ezt az appletünket letöltik. További
érdekessége a példának, hogy önálló alkalmazásként való futtatásra is felkészítettük.
Ennek a példának nincs önálló időbeli fejlődése, hanem csupán a játékos lépéseitől függő, azaz mondhatjuk,
hogy a program futását a játékostól érkező események irányítják. Viszont a labirintus megjelenítése már
grafikus.
3.1.3. A LabirintusJáték példa
A PC-s játékok tipikusan a teljes képernyőt uralják, ez az osztály a labirintus mikrovilágának egy olyan életre
keltését tartalmazza, amely ugyancsak képes erre! Ennek megfelelően a labirintus világának megjelenítése
grafikus, időbeli fejlődése pedig önálló. Ha például a hőst nem mozgatja a játékos, akkor is könnyen meglehet,
hogy az okos szörnyek felkutatják és hipp-hopp, néhány időegység után máris felfalták!
3.2. Mobiltelefonos példák
Ezen példák, mint nevük is mutatja, a mobiltelefonra történő Java alkalmazások fejlesztését vezetik be.
3.2.1. LabirintusMIDlet példa
Page 17
Bevezetés
xv Created by XMLmind XSL-FO Converter.
Ahogyan labirintus világunk appletként a böngészőbe töltődött, úgy töltődik MIDletként a mobiltelefonba. A
MIDlet objektumok azok a Java ME platformbeli objektumok, akik képesek létezni a mobiltelefonok Java
virtuális gépeiben.
Ez az osztály tehát arra ad példát, hogyan vihetjük át mobilra a teremtett labirintus mikrovilágunkat. A labirintus
világának megjelenítése itt is grafikus, időbeli fejlődése szintén önálló.
3.3. Hálózati példák
A hálózati példák, mint nevük is mutatja, a kézikönyvet több összekapcsolt gép mellett ülve feldolgozó
Olvasóknak nyújtják legoptimálisabban a programozás élményét. Ilyen lehet otthon két összekapcsolt gép, vagy
annak az oktatási intézménynek a tantermi hálózata, melyhez az Olvasó tanárként, hallgatóként, diákként
kötődik. De természetesen egyetlen, akár Windows, akár Linux operációs rendszerű gépet használva is
tesztelhetők a példák.
3.3.1. A LabirintusServlet példa
Hasonlóan az előző példához: ahogyan labirintus világunk appletként a böngészőbe töltődött, vagy éppen
MIDletként a mobiltelefonba, úgy töltődik Servletként a webszerverbe. A Servlet osztálybeli objektumok azok
a Java EE platformbeli objektumok, akik képesek a webszerverekben létezni. Tehát szemben az előző két
példával, objektumaink most nem a kliens oldalon, azaz nem a böngészőben és nem a mobiltelefon Java
virtuális gépében léteznek, hanem a szerver oldalon: a webszerverben.
A labirintus világának megjelenítése jelen példánál nem grafikus, hanem a böngészőbe történő HTML nyelvű,
azaz szöveges lapok küldésében merül ki, tehát elvben karakteres, de ebbe a szöveges válaszba megfelelő <img
Page 18
Bevezetés
xvi Created by XMLmind XSL-FO Converter.
scr=""> HTML parancsok küldésével nagyon könnyen grafikussá is tehető a labirintusunk böngészőbeli
megjelenítése. Továbbá a példa időfejlődése sem önálló.
3.3.2. A HálózatiLabirintus példa
Az informatikában van királyi út, abban az értelemben, hogy minél több programon keresztül csinál valamit a
programozó, annál egyszerűbb az élete. Gondoljunk csak arra, hogy mekkora erőfeszítés lenne gépi kódban
olyan programokat írni, amelyek különböző processzorú hardvereken működnek. Ennél kisebb erőfeszítés lenne
olyan programot írni, ami ugyanezeken a különböző hardvereken, de mondjuk mindegyik esetén Linux
operációs rendszerek alatt futna, mert már élvezhetnénk az operációs rendszer támogatását, ha egy magasabb
szintű nyelvet választanánk a fejlesztéshez, mondjuk a C nyelvet. De ha még a különböző hardvereken esetleg
különböző operációs rendszerek is futnak, viszont mindannyiukra megvan a Java Virtuális Gép (JVM - Java
Virtual Machine), akkor igazán a királyi úton járhatunk: Java programunk mindenhol futni fog, ahol fut a Java
Virtuális Gép. Ez a jelenség általában is megfigyelhető: az előző példában nem kellett foglalkoznunk a Servlet
objektumunk és a kliensek hálózati kapcsolatteremésének beprogramozásával, hanem csak azzal, hogy egy kész
kapcsolaton keresztül milyen, de már csupán a saját labirintusunkkal kapcsolatos adatokat akarunk küldeni a
kliensnek.
Ebben az értelemben a jelen példa lépés visszafelé, mert itt bizony foglalkozunk a hálózati kapcsolat
kiépítésének beprogramozásával, lévén, hogy a java.net csomag TCP-s hálózati osztályainak felhasználásával
a socket programozás absztrakciós szintjén dolgozunk itt. Ennek megfelelően kliensként a telnet programot
használjuk majd, azaz a megjelenítésünk karakteres lesz. Viszont a szerver oldalon a labirintust önálló
időfejlődéssel látjuk el.
3.3.3. A TávoliLabirintus példa
Abban az értelemben, ami szerint az előző példával visszafelé léptünk, most előre haladunk: itt nem
foglalkozunk kommunikációs kapcsolatok felépítésével, hanem csak a kommunikációnak a saját labirintus
Page 19
Bevezetés
xvii Created by XMLmind XSL-FO Converter.
példánk kapcsán érdekes részével. A kliens oldalon megszerezzük a távoli labirintus objektum referenciáját, ami
után már nincs akadálya, hogy a referencia után egy pontot téve meghívjuk a hivatkozott objektum egy
metódusát, igen távolról: de hoppá, ez az objektum egy másik, a távoli szerver oldal virtuális gépében van!
3.3.4. A KorbásLabirintus példa
Ezzel a példával tovább lépünk előre az absztrakciónak azon útján, amit a TCP/IP-s példa kapcsán kezdtünk el
boncolgatni. Immár az sem számít majd, hogy objektumaink milyen nyelven készültek, hanem csupán az, hogy
milyen üzeneteket lehet küldeni az objektumainknak vagy ha így jobban tetszik: milyen szolgáltatásokat
nyújtanak az objektumaink. Azt azért gyorsan megjegyezzük, hogy ettől persze mi továbbra is Java nyelven
dolgozunk, Java osztályokat készítünk, de példánknál maradva már leginkább csak arra kell figyelnünk, hogy
mit csinálnak a hősök a labirintusban...
Ez királyi út, de persze ára is van: sok programnak kell együtt és helyesen együtt működnie, hogy a programozó
kisebb terheket hordhasson a vállán...
3.3.5. A ElosztottLabirintus példa
Page 20
Bevezetés
xviii Created by XMLmind XSL-FO Converter.
Az eddigi példákat az jellemezte, hogy volt egy labirintus mikrovilágunk, aminek különféle használati eseteit
készítettük el. A jelen példa pedig arról szól, hogy a hős és a labirintus, sőt még a kincs és a szörny objektumok
is valóban különböző gépeken lehetnek! Például az ábra mutatta esetben a labirintus CORBA objektum a
172.21.0.152 IP számú és egy kincs a 172.21.0.17 IP számú gépen.
3.4. További példák
Találkozni fogunk még számos kisebb-nagyobb labirintus variáns feladattal, ezeket külön nem mutatjuk be itt,
mert e feladatok célja csupán valamilyen aktualitásra vagy apróságra, finomságra való rámutatás. Például a
LabirintusAlkalmazás osztály a Java Musztáng verziójától élő néhány GUI-val kapcsolatos újdonságot
(alkalmazásunk ikonjának elhelyezése az értesítési területre, indító képernyő a programunkhoz), vagy mondjuk
a GenerikusLabirintus osztály a - Java Tigris verziójától használható - generikus vagy éppen az iteráló for
ciklus alkalmazását mutatja be.
Illetve találkozni fogunk még számos kisebb-nagyobb olyan feladattal, amelyeket valamely, a kézikönyvben
érintett területtel kapcsolatban külön tárgyalunk. Ilyenek például a biológiai tárgyú szimulációs illetve számítási,
vagy a matematikai számolási (mint például a Mandelbrot halmaz tetszőleges részének kinagyításáról szóló,
vagy a Pi hexadecimális jegyeit kiszámoló) példák. A következő képet például a kézikönyvhöz készített
Mandelbrotos példaprogrammal generáltuk és mentettük el.
Page 21
Bevezetés
xix Created by XMLmind XSL-FO Converter.
3.4.1. A további önálló példák könyvjelzői
Itt a kézikönyvben szereplő önálló, azaz nem a labirintusos esettanulmányok részeiként szereplő példák közül
emelünk ki néhányat.
I. A Turing-féle gépek. (Programozás papíron.)
II. Számrendszerek, bitműveletek
i. Bitműveletek, kezdjük egy bináris dumppal. (Csak azoknak, akik szeretik a bitfaragást.)
ii. Kizáró vagyos titkosítás. (A táblánál és papíron is jól bemutatható bitműveletes példa, a gyakorlatban
használjunk PGP-t!)
iii. A kizáró vagyos titkosítás Swinges felületen. (Grafikus felülettel látjuk el az iménti példát.)
iv. Számrendszer átváltások. (Bármikor szükség lehet rá, például a kézikönyvben a Pi jegyeinél is
felhasználjuk majd.)
III. Véletlenszám generátorok, normális eloszlás
i. Normális, módosított polártranszformációval [29]. (Matematikusok első OO osztálya.)
ii. Hisztogram feladat. (Normalitás vizsgálat.)
Page 22
Bevezetés
xx Created by XMLmind XSL-FO Converter.
iii. Galton deszka kísérlet. (Szimulációs példa.)
IV. Fraktálok: Mandelbrot halmazok
i. Mandelbrot halmaz programja. (A szépség mellett GUI, szálkezelési, egér és billentyűzet
eseménykezelési példa.)
V. Pi közelítése
i. Gregory-Leibniz formula. (Pi/4 = 1 - 1/3 + 1/5 - 1/7 + ... valameddig egy ciklusban.)
ii. A Ramanujan és a Chudnovsky közelítő összegek formulái. (Hatékonyak, de számolni, a számolással
programozási élményt szerezni a 64 bites lebegőpontos aritmetika nem elegendő.)
iii. BBP (Bailey-Borwein-Plouffe) algoritmust: a Pi hexa jegyeinek számolása [361]. (Ezzel az algoritmussal
már saját PC gépünkön is nagy élményeket élhetünk át a Pi jegyeinek kutatásában!)
VI. Sejtautomaták
i. Sejtautomata szimuláció programja. (A híres Conway-féle életjáték és a Gosper-féle sikló ágyú.)
ii. Az Orch OR sejtautomata szimuláció részét demonstráló programja. (Az Orch OR a Penrose-Hameroff-
féle tudatmodell rövidítése.)
VII. Genomok, fehérjék
i. Genomi, aminosav vagy akár tetszőleges szekvenciák összehasonlítása. (Az aktualitáson túl egy Swinges,
legördülő menüs példa.)
És persze kombinálhatjuk is a példákat, mondjuk a genomi szekvenciák összehasonlítására készített
pontmátrixos programunkkal összehasonlíthatjuk a Pi hexadecimális kifejtésének első és második ezer jegyét,
vagy éppen a Pi ezer jegyét a második emberi kromoszóma első kétezer T, C, A, G jegyével. A programozni
tudás erejét éppen az adja, hogy ha a gépek tudnak válaszolni arra, amire éppen kíváncsiak lettünk, akkor egy
program formájában fel tudjuk nekik tenni a kérdésünket.
3.4.2. A példák jellege
A példaprogramok oktatási céllal készültek, azaz tipikusan valami olyan tulajdonságot mutatnak, amit velük
kapcsolatban a könyvben bemutatunk, kifejtünk. Például a labirintus elosztott változata azt akarja megmutatni,
hogy minden szereplő objektum külön számítógépen van. De ennél nem többet, ennek megfelelően nem
foglalkozik például - az egyébként természetesen felmerülő - hálózati terheléssel, szinkronizációs problémákkal,
hibakezeléssel, magas szereplő szám esetén a működőképességgel kapcsolatos kérdésekkel.
Ezért a példáknál magunk adunk meg például továbbfejlesztési feladatokat, tipikusan olyanokat, amiket mi
magunk már elkészítettünk vagy úgy gondoljuk, hogy el tudnánk készíteni a könyvben tárgyalt részek alapján.
De számos esetben megadjuk a megoldásokat, vagy legalább a megoldások lényegi részét.
3.4.3. Összefoglalás
A kézikönyvhöz készített programok tipikusan interaktív jellegűek. A labirintusos esettanulmányok példáira az
interaktivitás triviálisan teljesül, hiszen mindig a játékos mozgatja a labirintus hősét. A további programok
interaktívak vagy demonstratívak. Az előbbire példaként említhetjük a Mandelbrot halmazokat számoló,
nagyító, színező programokat, az utóbbiakra pedig például az Orch OR modelbelli mikrotubulus sejtautomata
üzemének demonstrálását.
Néhány interaktív jellegű példa alkalmas lehet arra, hogy más, nem konkrétan informatikai tárgyak oktatásában
is felhasználja a tanár Olvasó. Ilyen például a matematikai jellegű számítási példák között a Mandelbrot halmaz
zn+1 = zn2 + c iterációs számítási lépéseit grafikusan is megmutató példa. Vagy ilyenek a biológiai jellegű
számítási példák között a genomi szekvenciák összehasonlítására szolgáló példaprogramjaink, amelyeket
nemcsak a középiskolai, hanem a felsőoktatásbeli, nem konkrétan informatikai szaktárgyakhoz is
felhasználhatunk, hiszen ebben az irodalomban - például a [BIOINFORMATIKA] vagy [PROTEOMIKA]
hivatkozásokban - tipikus, hogy internetes programok címét adják meg a saját tapasztalatokra vágyóknak.
Page 23
Bevezetés
xxi Created by XMLmind XSL-FO Converter.
Remélhetőleg, vagy még inkább pontosabban, ha e könyv eléri célját, akkor az Olvasó kénye-kedve szerint tudja
majd kombinálni a példákat: ott, ahol nincs grafikus megjelenítés: készíteni tud majd ilyet, vagy ott, ahol nincs
önálló időfejlődés, fel tudja ruházni ezzel a példát. Ennek során persze felmerülhetnek további nehézségek,
amikre jelen bevezető könyv keretein belül nem térhettünk ki. Hogy csak néhány kombinációt említsünk: a
LabirintusServlet osztályhoz szeretnénk grafikus megjelenítő appletet vagy a LabirintusServlet
osztályba szeretnénk önálló időfejlődést. Nem konkrétan ez utóbbira, de az ilyen esetekre ajánljuk a Nyékyné
Gaizler Judit: Java 2 útikalauz programozóknak [JAVA KÖNYV] című mélyebb szakkönyvet, de
természetesen fordulhatnak bátran a szerzőkhöz is a [email protected] vagy a [email protected] címre írt,
Javát tanítok Olvasó tárgyú elektronikus levélben.
3.4.4. Platform
A szórakoztató háttér ismerete avagy előfeltételek a további
olvasáshoz
Ha a kedves Olvasó még nem látta, akkor a kézikönyv feldolgozásának érzelmi megalapozásaként
javasoljuk a Mátrix [MÁTRIX MOZI] trilógia első részének és a Mi a csudát tudunk a világról? című
[KVANTUM MOZI] film megnézését és átbeszélését az Olvasó környezetével: a barátokkal,
kollégákkal. Illetve javasolunk még némi számítógépes játékot, mondjuk például a DOOM [DOOM
JÁTÉK] FPS játékkal. További szórakoztató - s itt a szórakoztató alatt természetesen most nem az
ismeretterjesztőt értjük - források tekintetében például a [KAPCSOLAT MOZI] filmet vagy még
inkább a [KAPCSOLAT REGÉNY] regényt ajánlhatjuk. Ez utóbbira a könyv több tartalmi elemében is
hivatkozni fogunk.
Figyeltünk arra, hogy mind a Linux, mind a Windows operációs rendszert használó Olvasók a könyv szöveges
magyarázó és példaprogramos részeit egyaránt és teljesen egyformán élvezni tudják. Ez persze nem volt túl
nehéz, mivel főtémánk a Java, aminek egyik erőssége éppen a platformfüggetlenség. Ez azt jelenti, hogy
mindegy milyen operációs rendszer dolgozik alattunk, ha mi Javaban programozunk, programunk változtatás
nélkül futni fog mindkét platformon.
Operációs rendszer
Mindazonáltal azt tanácsoljuk az Olvasónak, hogy Windows rendszere mellé telepítsen fel egy Linuxot
is, mert a lazábban kapcsolódó olvasmányos részekbe igyekeztünk sok olyan finomságot is beleszőni,
amit egy Linux mellett ülve a legizgalmasabb kipróbálni. Mi például a Fedora (Core 5) Linux
operációs rendszert (http://fedora.redhat.com) használjuk, az Olvasónak is ezen rendszer használatát
javasoljuk.
Fedora Linux Core 6
A kézikönyv írásának vége felé közeledve elérhetővé vált a Fedora Core 6, amire természetesen mi is
frissítettünk, így immár Linux választása esetén e GNU/Linux rendszer használatát javasoljuk
(http://fedora.redhat.com).
Megjegyezhetjük, hogy az Fedora ötöshöz hasonlóan a Fedora hatosra is igaz, hogy 32 bites és 64 bites
rendszerekre egyaránt elérhető az imént megadott címen.
3.4.5. A példaprogramok szerkezete, kipróbálása és a kézikönyv jelölései
A könyvben szereplő minden programot a szerzők készítettek és le is futtattak tipikusan két gépen: egy Linuxos
és egy Windowsos PC-n. A szereplő hálózati példák kapcsán fontos kihangsúlyoznunk, hogy ezek mindegyike
kipróbálható egyetlen, hálózatba nem kapcsolt akár Linuxos, akár Windowsos gépen is.
A kézikönyv számos példájának bemutatása vagy az egyik vagy a másik különálló számítógépen történik.
Page 24
Bevezetés
xxii Created by XMLmind XSL-FO Converter.
A kézikönyv hálózati példái némelyikének bemutatása két gyors Ethernet hálózati kártyával felszerelt és
Ethernet kábellel összekötött, a 192.168.1.1 és a 192.168.1.2 fenntartott IP számokkal beállított
számítógépen történik. Esetünkben az egyik gép Linuxos, a másik Windowsos.
Az otthoni két gép összekapcsolása
Ha az Olvasó már rendelkezik két géppel, amiket az imént említett módon szeretne összekapcsolni,
akkor a két hálózati kártya és a kábel költsége ma már csupán néhány ezer forintnyi költségre rúg.
A kézikönyv néhány hálózati példájának bemutatása pedig egy az internethez is kapcsolt lokális hálózatban
történik.
Page 25
Bevezetés
xxiii Created by XMLmind XSL-FO Converter.
A kézikönyvben a futási eredményekkel kapcsolatos betétek szedése tipikusan a
[norbi@niobe ~]$
vagy az egészen rövid, egyetlen dollárjel
$
prompttal történik, ha a Linuxos és
C:\Documents and Settings\norbi>
prompttal, ha a Windowsos gépen történt a futás. Ez utóbbit néha nyomdatechnikai okokból majd a C:\...>
rövidített alakban írjuk, feltéve persze, hogy ez a rövidítés az értelmezést nem zavarja. Az előbbinél pedig
azokban az esetekben, amikor a tárgyalt példában szükséges lehet megkülönböztetnünk, hogy éppen melyik
gépen dolgozunk, a promptban a szokásos módon megkülönböztetjük, feltüntetjük a hoszt neveket (mint például
alább a niobe és az omega neveket) is.
Page 26
Bevezetés
xxiv Created by XMLmind XSL-FO Converter.
[norbi@niobe ~]$ itt a niobe nevű gépen vagyunk
[norbi@omega ~]$ itt pedig az omega nevű gépen dolgozunk éppen
A kézikönyv a példákkal kapcsolatos forráskód részleteket és teljes forrásprogramokat is tartalmaz. A források
mindkét esetben bőséges dokumentációval vannak ellátva, tehát külön elolvasásuk sem haszontalan. A teljes
programokat Linux és Windows rendszerek alatt is lefordítottuk és futtattuk, tehát ezek kipróbálásával,
futtatásával az Olvasónak - ha követi utasításainkat - sem támadhat áthatolhatatlan akadálya, főleg azért, mert a
kipróbálásról: fordításról, futtatásról, használatról mindig külön is szólunk, sőt számos esetben képeket is
bemutatunk. A teljes forrásprogramokat onnan is felismerheti a kedves Olvasó, hogy tipikusan a következő
jellegű Java dokumentációs megjegyzésekkel kezdődnek. Az alábbi sorok például a Galton deszkás kísérletes
szimulációs példánk kapcsán kifejlesztett GaltonDeszka osztályunkat definiáló GaltonDeszka.java
forrásállomány első sorai.
/*
* GaltonDeszka.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* A Galton deszka kísérletet szimuláló osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
*/
Azt is mutatja, hogy a forráskódot kijelölve azt a GaltonDeszka.java forrásállományba kell beillesztenie a
kedves Olvasónak az önálló felhasználásához, kísérletezéshez.
A magyar ékezetes betűk használatáról
A példaprogramokban igyekeztünk magyar ékezetes betűket használni. Igaz ez a példaprogramokat
alkotó osztályok neveire vagy például az osztályokban használt változónevekre. Sajnos e törekvésünk
során néhány alkalommal az ékezetes betűk használatából adódtak problémáink, ezekben az esetekben
majd ékezet nélküli osztálynevekkel találkozik a kedves Olvasó.
A források olvasásáról
A Java programozó API programozó, ennek megfelelően úgy kell használnia az API dokumentációt,
mint például a Linux/UNIX alatti programozónak a programozói kézikönyv - a második szintű manuál
- lapjait, amit például
[norbi@niobe ~]$ man 2 select
parancs kiadásával olvashatunk. Mi az API dokumentáció használatát annyiban tudjuk segíteni, hogy a
forrásokban felhasznált osztályok nevét mindig a teljes csomagnévvel minősítve írtuk, így például a
java.net.ServerSocket az is elárulja, hogy az Olvasó a szerver socketeket absztraháló
Page 27
Bevezetés
xxv Created by XMLmind XSL-FO Converter.
ServerSocket osztályt a java.net csomagban találja meg. Az API dokumentáció telepítéséről A Java
dokumentáció, azaz az API doksi letöltése című pontban olvashatunk.
4. Látványos Ars Poetica
„S mivel a játékok szocializációs funkciója alapvető jelentőségű, meglehet, hogy éppen az
információs ipar játéktermékei adják meg a döntő, visszavonhatatlan lökést a homo
informaticus evolúciójához.” —Mérő László
A következő ábra szerkezetével az emberiség tudásának egy lehetséges és persze erősen vitatható elrendezését
vázoltuk fel.
A nyilvánvaló visszacsatolások indukálta kérdésekkel - hogy mivel például az emberi lényről az
Orvostudomány doboz környékén beszélünk, de a matematika is az emberek fejében van..., vagy talán inkább a
platóni ideák világában? - nem foglalkoztunk.
Napjaink Informatika doboza az ábra Matematika dobázának közelében egy fiatal doboz lenne, de ha a madáchi
„szellem szemekkel” látnánk, akkor el tudjuk képzelni, hogy minden tudásunk egy absztrakt, de természetes
informatika ágba rajzolható, ez a mi ars poeticánk, amit Neumann [GÉP és AGY] utolsó könyvének utolsó
gondolata inspirál:
„Arra is rá kell mutatni, hogy ez az idegrendszeri nyelv nem is csekély valószínűséggel inkább
a korábban leírt értelemben vett rövid program, mint hosszú program. Meglehet, hogy amikor
matematikai fejtegetésekkel foglalkozunk, akkor egy olyan másodlagos nyelvről tárgyalunk,
amely ráépül a központi idegrendszer által ténylegesen használt elsődleges nyelvre.” —Neumann János
Page 28
Bevezetés
xxvi Created by XMLmind XSL-FO Converter.
Page 29
Created by XMLmind XSL-FO Converter.
I. rész - Programozás papíron
Ebben a részben nemcsak fejben, hanem papíron is dolgozunk, de ne ijedjünk meg: egyelőre nem azért nem
kapcsolunk be gépet, mert félünk tőle, hanem mert az itt szereplő gépeket nem lehet bekapcsolni, mivel ezek
egy platóni világban, pontosabban csak a mi képzeletünkben léteznek, működnek.
Továbbá a gyakorlati érdeklődésű Olvasónak is bátran ajánljuk ezt a részt, mert a Turing gépes környezetben
való programozás előszobája az algoritmikus információelméletnek. Ami, a mi olvasatunkban,a programozói
konstruktív szemlélet manifesztációja a tudományban.
A rész befejezéséül az objektumorientált (OO) világ, majd a hálózat, az internet programozóknak fontos
alapfogalmait tekintjük át.
Page 30
2 Created by XMLmind XSL-FO Converter.
1. fejezet - A programozásról
„Csak akkor értesz valamit, ha be tudod programozni. Te magad és nem valaki más! Ha nem tudod
beprogramozni, akkor csak úgy gondolod, hogy érted.” —Gregory Chaitin META MATH! The Quest for Omega
A tudomány fejlődésében egyre nagyobb szerepet kapnak az informatikai gondolkodásmódra épülő paradigmák.
E jelenség gyökere az informatikai gondolkodásmód erősen konstruktív jellege. Az informatikai ihletésű
paradigmák alapfogalma a számítások, a számítógépek könnyen kezelhető, precíz modellje a Turing gép. Ebben
az elméleti programozási fejezetben ezekkel a gondolatbeli gépekkel és programozásuk szépségeivel,
nehézségeivel ismerkedünk meg. Elméleti erőfeszítéseink zárásaként bemutatjuk a Turing gépek több,
filozofikusan is nagyon izgalmas felhasználását, amik többek között a matematika megalapozásának kérdéséhez
is elvezethetik a kedves Olvasót. Majd a gyakorlat, a Java programozás felé vesszük az irányt, bevezetve a Java
SE, Java ME OO világ és végül az internet programozásának alapfogalmait.
Ebben az elméletiből-gyakorlati tárgyalásba forduló, programozást bevezető részben
• megismerhetjük a számítógépek, számítások elméleti modelljét: a Turing gépet
• a Turing gépekhez kapcsolódó legfontosabb eredményeket
• egy a Turing gépekre épülő bonyolultsági mértéket
• a végtelen ciklusok elkerülésének Omega valószínűséget
• végül az eddigi elméleti erőfeszítéseink koronájaként a Gödel és Chaitin-féle inkompletibilitási tételeket
• majd tovább lépve a gyakorlat felé: megismerhetjük a Java SE világát
• és a Java ME világát
• végül az internet és a hálózati programozás alapfogalmait vezetjük be
1. A programozás filozófiája
„Elképzelte, ahogy a kis adatcsomag keresztüláramlik a hálózati kábelen és létrehozza a
képernyőn látható tengerészgyalogost. - A jobb oldali számítógépre pillantott és figyelte a
most külső szemszögből látható karakterét, ahogy átfut a képernyőn. Fantasztikus világot
teremtett, most pedig életre keltette.” —David Kushner
Az előszóban említettük, hogy a programozásra gondolva olykor valami misztikus, mámoros érzés járja át a
programozót...
Foglalkozzunk most tovább kicsit ezzel az érzéssel! Mert foglalkozni kell vele: hogyan lehet a diákokban
felépíteni azokat a mentális struktúrákat, amelyek majd rezonálni képesek erre az érzésre? Hol élhetjük hát át ezt
az érzést? A Linux különösen sok alkalmat ad rá: például a kernelfordításnál - a kernelfordítás leírását lásd a
[PROGRAMOZÓ PÁTERNOSZTER JEGYZET]-ben - amikor a C források éppen fordulnak és tudjuk, hogy
néhány perc múlva már ezzel az éppen most lefordított kernellel fogjuk újra bebootolni gépünket, hogy az
általunk, éppen most, a rendszerre szabott szoftver vezérelje azt. De sokszor még gép közelébe sem kell
mennünk, hogy átéljük a szóban forgó élményt. Elég beülni a moziba vagy betenni a dvd lemezt és megnézni az
életünkben felvirradt információs-programozói civilizáció csúcstermékét, az immár kultuszfilmet -
programozóknak pedig kötelező szakmai mozit - a Mátrix [MÁTRIX MOZI] trilógiát. Ennek az alkotásnak a
gyökereitől elvitathatatlan, hogy a virtuális valóságon keresztül az informatikába nyúlnak.
Az információs-programozói civilizáció kialakulásával párhuzamosan egyre mélyebbre és mélyebbre nyúlnak
az informatika fájának gyökerei is. Az Alan Turing által képzeletben, Neumann János vezetésével gyakorlatban
létrehozott, a gondolkodás formalizált lépéseit imitáló gépek utódainak mai hekkerei (hackerei) már az emberi
tudatot, a kvantumfizikai valóságot akarják programozni...
Page 31
A programozásról
3 Created by XMLmind XSL-FO Converter.
Kezdjük hát - hogy stílszerűek legyünk - kedves Olvasó, „menjünk fel adásszintre”!
1.1. A programozás alapjai
„Valóban a matematika nyelvén írták a világegyetemet, mint azt Galilei gondolta? Én
hajlamosabb vagyok azt hinni, hogy inkább ez az egyetlen nyelv, amin megpróbálhatjuk
elolvasni.” —Stanislas Dehaene
Mi hát az a nyelv, amit a ma programozójának beszélnie kell? A legalapvetőbb és egyben az átlagos
programozó számára garantáltan a legeslegfelhasználhatatlanabb nyelv a Turing gépeké. Mindig
megnevettethetik az érdeklődő Olvasót azok a viccek, amik poéntartalma a: „Hol van a Turing gép kiállítva?”
beugratós kérdés variánsa. Mert ezek a gépek csupán a matematikai képzeletünkben léteznek. De csak azért ne
becsüljük le ezeket a konstrukciókat, mert mint írtuk, az iparban nem felhasználhatók. Neumann ENIAC
(Elektronikus Numerikus Integráló és Számológép) gépéhez - ami a maga idejében ipari és tudományos
területről egyaránt érkező, számos numerikus jellegű feladatot oldott meg - ma már nem találnánk programozót,
mert a gépek és programozásuk időközben annyira megváltozott. Nem így a Turing gépek, azok ma is
ugyanazok és ugyanúgy programozandók, mint ahogyan Turing annak idején megálmodta és programozta
[TURING CIKK] őket.
A „Hol van a Turing gép kiállítva?” kérdés története
A Debreceni Egyetem helyi legendáriuma ezt a kérdést Szabó József professzor úr kedvelt, tréfás
beugratós, államvizsgán elhangzó kérdéseként jegyzi. A Juhász Istvánnal történő közös
vizsgáztatásakor történt meg az az eset, amikor a hallgató azt felelte: „Otthon az iskolámban”. Ugyanis
egy levelezős általános iskolai tanár Juhász István biztatására megépített két fizikai reprezentációt is,
amelyen a gyerekek nagy lelkesedéssel „Turing-programoztak” [TURING GÉP REPREZENTÁCIÓ].
1.1.1. Algoritmikus kérdések
„Bele kell majd nyugodnunk abba a ténybe, hogy értelmünk erőfeszítései nem adhatnak olyan
teljes képet a világról, amilyet elérni - erőfeszítésektől mentesen, könnyű elmélkedéssel - a
görögök álma volt.” —Wigner Jenő
A következő néhány pontban leverünk gondolkodásunk sodrába néhány olyan mentális cölöpöt, melyekbe
kapaszkodva meg tudjuk vetni lábunkat, ha filozofálni támad kedvünk a programozásról magáról. Megismerjük
az univerzális Turing gépeket és a megállási, azaz a végtelen ciklusok elkerülésének problematikáját, továbbá
egy immár tisztán a programozásra épülő, mindenféle bonyolultságokat összehasonlítani képes fogalmat: a
Chaitin-Kolmogorov-Solomonoff bonyolultságot. Végül a programozás orientált információelmélet
inkompletibilitási tételeit mutatjuk be. Aki programozó akar lenni, annak ezeket a fogalmakat nem árt ismerni.
Aki pedig komolyan akar hekkelni a témában, annak ezeket a fogalmakat ismerni kell, hát még annak, aki - e
pont bevezető idézetének értelmében - görög akar lenni! Ez utóbbi tréfás tagmondatot az inspirálja, hogy
véleményünk szerint egy átlagos programozó a véletlen sorozatok témában rövid idő alatt, szemléletében is
mély tudásra tehet szert, még hasonló tudás megszerzése a hegy matematikai statisztikai oldalán mászva nagyon
nagy nehézségekkel járna számára (a metaforikusan említett hegy statisztikai ösvénynek a leírását a [KNUTH 2.
KÖNYV] könyvben olvashatja a matematikai érdeklődésű Olvasó).
1.1.1.1. A Turing-féle gépek
„Az egyik legjellemzőbb emberi vonás a kíváncsiság. A végtelenségig nem dacolhatsz vele.” —Arthur C. Clarke
A Turing gépeket matematikailag egyszerűen, szépen és pontosan le lehet írni, most mégis inkább egy rajzot
készítünk, mert azt feltételezzük, hogy ez az első találkozásunk ezekkel a gépekkel. Íme legyen az első Turing
hardverünk a következő!
Page 32
A programozásról
4 Created by XMLmind XSL-FO Converter.
Bemutatott hardverünk leírása: a memória végtelen sok cellából áll, jelen Turing hardverünkben egy
memóriacella három értéket hordozhat. A # jelöli, hogy üres a cella és lehet még benne a 0 vagy az 1 számjegy.
A vezérlőegység képes beolvasni és írni az I/O fej alatti memória cellát és az állapot regiszterét, továbbá jobbra
és balra lépkedni, de akár helyben is maradni.
Jöjjön a szoftver! A Turing gépet programozhatjuk szövegesen vagy grafikusan. Egy példán keresztül lássuk
először a szöveges módot:
1. Ha Lépked állapotban vagyok és 1-et olvasok, akkor 1-et írok, Lépked állapotban maradok és jobbra lépek!
Röviden: (Lépked, 1) -> (Lépked, 1, ->)
2. Ha Lépked állapotban vagyok és 0-t olvasok, akkor 0-t írok, Lépked állapotban maradok és jobbra lépek!
Röviden: (Lépked, 0) -> (Lépked, 0, ->)
3. Ha Lépked állapotban vagyok és #-et olvasok, akkor #-et írok, Lépked állapotban maradok és jobbra lépek!
Röviden: (Lépked, #) -> (Lépked, #, ->)
4. Kezdetben legyek Lépked állapotban, az input szó első betűjén állva!
Az utasítások általános formája a Turing gép utasításciklusának alábbi (végrehajtása előtt) -> (végrehajtása után)
formájában megadva a következő:
(állapotban vagyok, mit olvasok) -> (állapotban leszek, mit írok, merre lépek)
A program megadásának grafikus módja egy gráf, az úgynevezett állapot-átmenet gráf megadása. A gráf csúcsai
a gép lehetséges állapotai, élei a következő alakúak.
A „program gráf” működése: megnézzük,
hogy milyen állapotban vagyunk és éppen mit olvasunk, majd az állapotunknak megfelelő csúcsból az ilyen (mit
olvasunk, , ) alakú címkével jelzett kivezető élt keresünk és azon megyünk tovább. Ha esetleg nincs ilyen él az
állapotunknak megfelelő csúcsból, akkor a gép megáll.
Adjuk meg most az imént szereplő, szövegesen leírt szoftvert grafikus formában!
Page 33
A programozásról
5 Created by XMLmind XSL-FO Converter.
Jelen példánkban az ötödik lépés után már mindig a (#, #, ->) élen
utazunk tovább ugyanoda és mindig ugyanazt, a # jelet olvassuk ott újra, tehát ...
Tehát mit csinál ez a program? Kövessük végig gondolatban, azaz futtassuk le! Remélem nem futtattuk órákig,
mert bizonyára tapasztaltuk, hogy a program soha nem áll le, ez bizony végiglépked az input szó betűin, majd
tovább az üres jeleken, s amit olvas azt visszaírja... ez egy végtelen ciklus: egy olyan szituáció amikor a gép
soha nem áll le.
Nézzünk még egy példát, most a hardver legyen egészen hasonló, mint az előző gépbe épített, de immár egy
állapottal bővebb az állapotaink halmaza:
A szoftver is legyen hasonló, mégpedig grafikus alakban megadva a következő.
Mit csinál a gép? Végigmegy az inputon és a végére ír egy nullát. A szalagon példaként álló 101 szóból az 1010
szót készíti el. Az 11 szóból az 110 szót,
az 1100110101101111001111111111 szóból
az 11001101011011110011111111110 szót, azaz minden bemenő bináris szó végéhez hozzáfűz egy nullát, tehát
kettővel szorozza az input szót: például 101 = 5, 1010 = 10. Az új nulla beírásakor átmegy a Vég állapotba, ahol
aztán meg is áll, mert nincs olyan programutasítás, ami most alkalmazható lenne, hiszen ebből az állapotból
semmilyen él nem vezet ki (szövegesen: nincs olyan programsor, ami arra válaszolna, mit kell csinálni a
vezérlésnek, ha Vég állapotban van és # betűt olvas, így tanácstalanságában a gép megáll).
Készítsünk egy harmadik gépet is, további továbbfejlesztésként: ha az üres szó van induláskor a szalagon, azaz,
ha input nélkül futtatjuk a gépet, akkor ne csináljon semmit, illetve ha esetleg vannak a bináris szó előtt vezető
Page 34
A programozásról
6 Created by XMLmind XSL-FO Converter.
nullák, akkor azokat törölje le. Egyébként ugyanúgy kettővel szorozzon!
E harmadik gép szoftvere:
Negyedik gyakorló gépünk üzemeljen kicsit más jelleggel, legyen feladata az input szóról eldönteni, hogy
rendelkezik-e valamilyen tulajdonsággal. Döntse el, hogy az inputként binárisan lekódolt szám kettővel
osztható-e. Szokás szerint minden azzal kezdődik, hogy a programozó kitalálja az algoritmust: igen a válasz, ha
a szám bináris kódja a 0 jeggyel végződik, nem, ha az 1 számjeggyel, vagy nincs input szó.
A szoftvert úgy szervezzük, hogy a gép az Elfogadó állapotában álljon meg, ha az input szó osztható kettővel,
illetve ellenkezőleg, az Elutasító állapotában álljon meg, ha nem osztható. A szoftver legyen tehát a következő:
Page 35
A programozásról
7 Created by XMLmind XSL-FO Converter.
1.1.1.1.1. Az univerzális Turing gépek
A fenti három példa már jól mutatja, hogy minden feladatra külön Turing-féle számítógépet kell konstruálnunk.
Meg kell adnunk, hogy milyen betűk lehetnek a szalagon, milyen állapotokban lehet a gép, mi a kezdő és
végállapota - ezek voltak a hardver kérdések - és milyen (állapot, olvasott) -> (állapot, írni, lépésirány)
programutasításai vannak - ezek voltak a felmerülő szoftveres kérdések. Egy nagyon fontos, alapvető, de most
bizonyítás nélkül ismertetett tétel azt mondja, hogy létezik olyan Turing gép, ami képes bármely más Turing-
féle gépet szimulálni. (A bizonyítás szó kapcsán rámutathatunk, hogy ezen a bizonyításon a megfelelő Turing
gép megkonstruálását kell érteni!) Visszatérve a szimulációs tételhez, ez azt jelenti, hogy ezt a speciális Turing
gépet egy másik Turing gép és e másik Turing gép inputjából készített inputtal indítva ugyanazt az eredményt
fogja produkálni, ugyanúgy fog működni, mint önmagában futtatva a másik Turing gép az inputjával. Ezeket a
speciális Turing gépeket univerzális Turing gépeknek nevezzük. Ezek a mi mai számítógépeink megfelelő,
elméleti modelljei, mert innentől nem kell minden feladathoz külön Turing gép, hanem veszünk egy univerzális
Turing gépet és annak inputként (tehát adatként) beadhatunk egy megfelelően lekódolt Turing gépet (mint
végrehajtandó programot) és annak inputját (mint adatot).
Az egyszerű Turing gépet rajzban így festettük le:
Ennek megfelelően az Univerzális Turing gépeket pedig így rajzolhatjuk le:
Mindkét géptípus esetén a továbbiak során röviden majd azt is rajzoljuk, hogy
Page 36
A programozásról
8 Created by XMLmind XSL-FO Converter.
Ami alatt azt értjük, hogy a T gép szalagján az x szó az input, az U gép szalagján pedig egy T gép és a T gép x
inputjának egyesítésével előálló szó az input.
1.1.1.2. A Turing gép piac
„Senki sem űzhet ki minket a Cantor által teremtett paradicsomból” — David Hilbert [STEWART KÖNYV]
Miért bírnak alapvető fontossággal a Turing-féle gépek? Mert tapasztalataink alapján úgy gondoljuk, hogy
azokat a feladatokat lehet digitális számítógéppel, tehát valamilyen programozási nyelven megírt algoritmussal
megoldani, amit Turing géppel is meg lehet oldani és megfordítva: amely feladatokat nem tudunk Turing géppel
megoldani, azt algoritmussal, azaz valamely digitális számítógépre valamilyen nyelven megírt semmilyen
programmal sem tudunk megoldani. Ezt a tapasztalatunkat nevezzük Church-Turing tézisnek. A tézissel
kapcsolatban további olvasmányként a [DRAGÁLIN KÖNYV] logika vagy az [ALGORITMUSOK KÖNYV],
illetve a [LOVÁSZ KÖNYV] algoritmuselméleti tankönyveket ajánljuk.
Tehát amit meg tudok írni C-ben vagy Javaban, azt elvben Turing géppel is meg tudnám csinálni, ki tudnám
számítani. Meglehet, hogy ennek a megfelelő Turing gépnek az elkészítése hatalmas fáradtság lenne, mint
ahogyan például a megoldást adó x86 assembly nyelvű program változatnak megírása is jóval nagyobb munka
lenne az eredeti C vagy Java változat megírásánál. Futási ideje is hihetetlenül megnőne, lévén a Turing gép a mi
fejünkben működik, magam interpretálom, játszom le sorban a gép működésének lépéseit... de a lényeg, hogy
elvben nincs különbség a Java nyelvű és a Turing program között. Azért a programozói fizetés tekintetében
inkább a Java nyelv gyakorlására hangolnám a tipikus Olvasót, semmint a Turing programozásra.
1.1.1.3. Végtelen ciklus, avagy a megállás problémája
„Ne kérdd
Tovább a titkot, mit jótékonyan
Takart el istenkéz vágyó szemedtől.
Ha látnád, a földön múlékonyan
Pihen csak lelked, s túl örök idő vár:
Erény nem volna itt szenvedni többé.
Ha látnád, a por lelkedet felissza:
Mi sarkantyúzna, nagy eszmék miatt”
—Madách Imre
A végtelen ciklusok felbukkanása két területen különösen gyakori, ezek a területek a szerver oldali programozás
és az algoritmusokról szóló elméleti vizsgálatok. Ebben a pontban még ez utóbbival foglalkozunk: építünk egy
megépíthetetlen Turing komputert!
Tegyük fel, hogy van egy olyan algoritmusunk - azaz a Church-Turing tézis értelmében van egy olyan Turing
gépünk - ami egy másik algoritmusról, azaz Turing gépről meg tudja mondani, hogy megáll-e majd. Tehát, hogy
nem kerül végtelen ciklusba. Nevezzük ezt a feltételezett speciális gépet M gépnek és felépítése legyen ilyen:
Page 37
A programozásról
9 Created by XMLmind XSL-FO Converter.
Ez az M gép az inputjaként kódolva megkapott tetszőleges T Turing gépről el tudja dönteni, hogy az meg fog-e
állni vagy végtelen ciklusba fog esni. Ezt tételeztük fel, hogy van ilyen M (Megállást megvizsgálni tudó) gép.
Nézzük meg az M gép működését egy konkrét T gépre:
i. ha a T gép megálló gép, akkor az M a kékkel jelölt úton működik és előbb-utóbb az elfogadó állapotában áll
meg
ii. ha a T gép végtelen ciklusba eső gép, akkor az M a pirossal jelölt úton működik és előbb-utóbb az elutasító
állapotában áll meg.
Ennek a feltételezetten létező M gépnek a felhasználásával építsük meg a még nagyobb E gépet a következő
tervrajz alapján!
Játsszuk végig az E (Ellentmondó gép) működését egy tetszőlegesen választott, konkrét T inputra:
i. ha a T gép megálló gép, akkor az M a kékkel jelölt úton működik és előbb-utóbb az elfogadó állapotába
megy, ahonnan bármit olvas, visszaírja, helyben marad a fej és átmegy a gép a végtelen ciklus állapotba,
ahogyan a neve is utalja: bármit olvas, visszaírja, helyben marad a fej, azaz ez egy végtelen ciklus! Tehát
nem áll meg az E gép.
ii. ha a T gép végtelen ciklusba eső gép, akkor az M a pirossal jelölt úton működik és előbb-utóbb az elutasító
állapotába megy, ahonnan bármit olvas, visszaírja, helyben marad a fej és átmegy a gép a Megáll állapotába,
ahogy mint neve is erre utal megáll. Tehát megáll az E gép.
Jöjjön most a trükk: hogy működik az E gép, ha a saját maga lekódolása az inputja? Tehát ha az E gépet az T=E
inputtal indítjuk el?
i. Ha az E gép megálló gép, akkor az M a kékkel jelölt úton működik és előbb-utóbb az elfogadó állapotába
megy, ahonnan bármit olvas, visszaírja, helyben marad a fej és átmegy a gép a végtelen ciklus állapotba,
ahogyan a neve is utalja: bármit olvas, visszaírja, helyben marad a fej, azaz ez egy végtelen ciklus! Tehát
nem áll meg az E gép, hanem végtelen ciklusba esik.
ii. Ha az E gép végtelen ciklusba eső gép, akkor az M a pirossal jelölt úton működik és előbb-utóbb az elutasító
állapotába megy, ahonnan bármit olvas, visszaírja, helyben marad a fej és átmegy a gép a Megáll állapotába,
ahogy mint neve is erre utal megáll. Tehát megáll az E gép.
Emeljük ki az eredményt:
i. Ha az E gép megálló gép, akkor nem áll meg az E gép, hanem végtelen ciklusba esik.
ii. Ha az E gép végtelen ciklusba eső gép, akkor megáll az E gép.
Page 38
A programozásról
10 Created by XMLmind XSL-FO Converter.
Hoppá! Döbbenetes, nem igaz? Jó kis ellentmondás, ez Turing nagy felfedezése, hogy a megállási probléma
nem dönthető el! Mert az ellentmondásnak csak az lehet a feloldása, hogy a feltételezett M gépünk nem
létezhet! Az E gép már csupán egy légvár vagy még inkább csak egy álomkép volt, mert alkatrésze az M
(Megállást megvizsgálni tudó) gép nem létezhet, mert ha létezne, abból az imént demonstrált ellentmondás
következne. Tehát nincs olyan algoritmus, ami meg tudná mondani, hogy az inputjaként kapott tetszőleges
Turing gép megáll-e vagy végtelen ciklusba esik. Ez nem azt jelenti, hogy a
#include <stdio.h>
int
main(void)
{
printf("Hello, Vilag!\n");
return 0;
}
C programról nem tudom kijelenteni, hogy megáll, hiszen hogyne állna meg.
S nem azt jelenti, hogy a
int
main(void)
{
for(;;)
;
}
C programról nem tudom kijelenteni, hogy végtelen ciklus, hiszen hogyne lenne az. Hanem azt jelenti, hogy
nincs olyan algoritmus, ami a fenti két programról és ezekkel együtt tetszőleges programról meg tudná mondani,
hogy meg fog-e állni vagy végtelen ciklusba esik. Tehát nincs olyan program, aminek a bemenetéül megadva a
fenti két program egyikét, vagy egy tetszőleges más programot, a program lefutna és kiírná, hogy meg fog-e
állni az inputként kapott program vagy végtelen ciklusba esik.
1.1.1.4. A Chaitin-Kolmogorov bonyolultság
„Kövesd a fehér nyulat!” —MÁTRIX
Figyelmeztetés a Javaban kezdő Olvasóknak
Ebben a pontban felhasználunk néhány Java nyelvű programot. Ezek igen egyszerűek, de annak, aki
még nem ismeri a Java nyelvet, meglehet, teljesen olvashatatlanok, ismeretlen jelentésűek. Fontos,
hogy ők még véletlenül se tekintsenek úgy ezekre a programokra, mint Java bevezetésre, mint az első
Java programjaikra, mert ezek nem erre a célra készültek. Most csupán annyi a fontos, hogy a program
mit ír ki és hány karakterből áll. Az előbbit mi leírjuk, az utóbbit könnyen megszámolhatja a kezdő
Olvasó is, természetesen a program bármilyen feldolgozása, megértése nélkül.
1.1. példa - Első sorozat: 1000 nulla
Tekintsük a következő 1000 darab nullából álló mondatot!
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
Page 39
A programozásról
11 Created by XMLmind XSL-FO Converter.
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000
Nézzünk most meg az ElsőSorozat1 nevű programot, ami éppen ezt a sztringet nyomtatja ki:
public class ElsőSorozat1 {
public static void main(String[] args) {
System.out.print("000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000
0000000000");
}
}
Ez a program 1082 karakterből áll (az egyszerűség kedvéért a szóközöket nem számoljuk).
1.2. példa - Első sorozat, máshogy: 1000 nulla
Nézzünk meg az ElsőSorozat2 nevű programot, ami szintén ugyanezt a sztringet nyomtatja ki:
public class ElsőSorozat2 {
public static void main(String[] args) {
for(int i=0; i<1000; ++i)
System.out.print(0);
}
}
Ez a program már csupán 103 karakterből áll. Ebben az egyszerű esetben könnyen látható, hogy néhány
karakterrel rövidebb Java forrásprogramot is lehetne készíteni - de ezt általában nem lehet tudni - nekünk
mindenesetre most ez a legrövidebb olyan programunk, ami kinyomtatja az 1000 darab nullát.
1.3. példa - Második sorozat: 01010101 ...
Tekintsük az újabb 1000 darab karakterből álló mondatot:
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
Page 40
A programozásról
12 Created by XMLmind XSL-FO Converter.
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
0101010101010101010101010101010101010101
Ezt a sztringet nyomtatja ki a MásodikSorozat1 nevű program:
public class MásodikSorozat1 {
public static void main(String[] args) {
for(int i=0; i<500; ++i)
System.out.print("01");
}
}
Ez a program 108 karakterből áll.
1.4. példa - Harmadik sorozat: 00000000001 ... 10000000000
Tekintsük a harmadik 1000 darab karakterből álló mondatot:
00000000001111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111110000000000
Ezt a sztringet nyomtatja ki a HarmadikSorozat1 nevű program:
public class HarmadikSorozat1 {
public static void main(String[] args) {
System.out.print("0000000000");
for(int i = 0; i<980; ++i)
System.out.print("1");
System.out.print("0000000000");
}
}
Page 41
A programozásról
13 Created by XMLmind XSL-FO Converter.
Ez a program 170 karakterből áll.
1.5. példa - Negyedik sorozat: egyre több 1 jegy két nulla között
Tekintsük a negyedik 1000 darab karakterből álló mondatot, 0 számjegyek között egyre több 1 számjegy van:
00101101110111101111101111110111111101111111101111111110111111111101111111111101
11111111111011111111111110111111111111110111111111111111011111111111111110111111
11111111111011111111111111111101111111111111111111011111111111111111111011111111
11111111111110111111111111111111111101111111111111111111111101111111111111111111
11111011111111111111111111111110111111111111111111111111110111111111111111111111
11111101111111111111111111111111111011111111111111111111111111111011111111111111
11111111111111110111111111111111111111111111111101111111111111111111111111111111
10111111111111111111111111111111111011111111111111111111111111111111110111111111
11111111111111111111111111011111111111111111111111111111111111101111111111111111
11111111111111111111101111111111111111111111111111111111111101111111111111111111
11111111111111111111011111111111111111111111111111111111111110111111111111111111
11111111111111111111111011111111111111111111111111111111111111111101111111111111
1111111111111111111111111111111111111111
Ezt a sztringet nyomtatja ki a NegyedikSorozat1 nevű program:
public class NegyedikSorozat1 {
public static void main(String[] args) {
for(int i=0; i<44; ++i) {
System.out.print(0);
for(int j=0; j<i; ++j)
System.out.print(1);
}
System.out.print("1111111111");
}
}
Ez a program 177 karakterből áll.
1.6. példa - Ötödik sorozat: pszeudo véletlen
Tekintsük az ötödik 1000 darab karakterből álló, meglehetősen rendszertelennek tűnő mondatot:
01011010000010010000111000111110111100010100100111101001000000010100100011101010
01111111010000001001000011011000101011110010011010100010010000010110011001000101
01100110010001101001110000110101110011111011010001101110001010010000001110011110
00101001001111100100111101101111000110111011010011110000101001010000111110000100
01110110011100101011000000011011111011011101101110110110000101010110011000100111
00111010110001111010011110100000011110101011100001101100001111001001010101000000
01111111101100000100001000001000000011111000101010111111000000010001111101110100
11010001011010011111010100111010100110101101011011110011001101011011011101101111
00110111000100101011111111111111000001010111100110100101001101111011001000100100
10111011000111101110000110010011010100110111101011001011101011011110110100100011
10001111100010100010111111100100010010111010000001110001101010101000001100100100
10011110000110110111001111110101101011110101100011001100001011010100001111111010
Page 42
A programozásról
14 Created by XMLmind XSL-FO Converter.
0000100111011101010100101101001110010001
Ezt a sztringet nyomtatja ki az ÖtödikSorozat1 nevű program:
public class ÖtödikSorozat1 {
public static void main(String[] args) {
System.out.print("010110100000100100001110001111101111000101001001111010
010000000101001000111010100111111101000000100100001101100010101111001001
101010001001000001011001100100010101100110010001101001110000110101110011
111011010001101110001010010000001110011110001010010011111001001111011011
110001101110110100111100001010010100001111100001000111011001110010101100
000001101111101101110110111011011000010101011001100010011100111010110001
111010011110100000011110101011100001101100001111001001010101000000011111
111011000001000010000010000000111110001010101111110000000100011111011101
001101000101101001111101010011101010011010110101101111001100110101101101
110110111100110111000100101011111111111111000001010111100110100101001101
111011001000100100101110110001111011100001100100110101001101111010110010
111010110111101101001000111000111110001010001011111110010001001011101000
000111000110101010100000110010010010011110000110110111001111110101101011
110101100011001100001011010100001111111010000010011101110101010010110100
1110010001");
}
}
Ez a program 1084 karakterből áll.
Összegezzük eredményeinket!
1.1. táblázat - A bonyolultságmérő programok összefoglalása.
SOROZAT PROGRAM MÉRET
Első sorozat ElsőSorozat1 1082
ElsőSorozat2 103
Második sorozat MásodikSorozat1 108
Harmadik sorozat HarmadikSorozat1 170
Negyedik sorozat NegyedikSorozat1 177
Ötödik sorozat ÖtödikSorozat1 1084
Chaitin után azt a sorozatot tekintjük kevésbé bonyolultnak, amit rövidebb programmal sikerült generálni. E
definíció alapján az iménti sorozatok egyre bonyolultabbak voltak. Bár az elsőre is adtunk egy 1082 karakteres
programot, de rögtön utána találtunk egy csupán 103 karakteres programot. Intuíciónkkal jó összhangban van ez
a megközelítés, mert az utolsó sorozatra nemigen tudunk az 1084 hosszú programnál rövidebbet írni, mert nincs
benne olyan szabályosság, amit beprogramozva tömörítést érhetnénk el.
1.7. példa - Bármely sorozat: egy másoló program
Vagy mégis? Tekintsük most meg a következő másoló programot!
Page 43
A programozásról
15 Created by XMLmind XSL-FO Converter.
public class TetszőlegesSorozat1 {
public static void main(String[] args) throws Exception {
int i = 0;
while((i=System.in.read()) != -1)
System.out.printf("%c", i);
}
}
Működése a szokásos: abban áll, hogy a bemenetét a kimenetére másolja. Így ezzel is ki tudom nyomtatni
bármelyik sorozatot! A program hossza pedig csak 147 betű!
A program inputjaként megkapva az ötödik sorozatot:
C:\...> echo 0101101000001001000011100011111011110001010010011110100100000001010
01000111010100111111101000000100100001101100010101111001001101010001001000001011
00110010001010110011001000110100111000011010111001111101101000110111000101001000
00011100111100010100100111110010011110110111100011011101101001111000010100101000
01111100001000111011001110010101100000001101111101101110110111011011000010101011
00110001001110011101011000111101001111010000001111010101110000110110000111100100
10101010000000111111110110000010000100000100000001111100010101011111100000001000
11111011101001101000101101001111101010011101010011010110101101111001100110101101
10111011011110011011100010010101111111111111100000101011110011010010100110111101
10010001001001011101100011110111000011001001101010011011110101100101110101101111
01101001000111000111110001010001011111110010001001011101000000111000110101010100
00011001001001001111000011011011100111111010110101111010110001100110000101101010
00011111110100000100111011101010100101101001110010001|java TetszőlegesSorozat1
Ha megnyomva az entert lefuttatjuk, akkor kiírja az ötödik sorozatunkat. Ezért a fenti bonyolultság
definíciónkat ki kell bővítenünk, mert emlékezzünk vissza, most egy 147 karakteres programmal és egy kis
trükkel - nevezetesen, hogy inputként adtuk meg a kívánt sorozatot - sikerült generálnunk az ötödik
sorozatunkat.
Egy x sorozat Chaitin-Kolmogorov bonyolultságának annak a program kódjának és inputjának az együttes
hosszát nevezzük, ami program kinyomtatja x-et és az ilyen programok között a legkisebb. Ennek megfelelően
korábbi összefoglaló táblázatunk most az alábbi alakot ölti.
1.2. táblázat - A bonyolultságmérő programok és bemenetük együttes összefoglalása.
SOROZAT PROGRAM KÓD MÉRET INPUT
MÉRET
A bonyolultság felső
becslése
Első sorozat ElsőSorozat1 1082 0 1082
ElsőSorozat2 103 0 103
TetszőlegesSorozat1 147 1000 1147
Második sorozat MásodikSorozat1 108 0 108
TetszőlegesSorozat1 147 1000 1147
Page 44
A programozásról
16 Created by XMLmind XSL-FO Converter.
SOROZAT PROGRAM KÓD MÉRET INPUT
MÉRET
A bonyolultság felső
becslése
Harmadik sorozat HarmadikSorozat1 170 0 170
TetszőlegesSorozat1 147 1000 1147
Negyedik sorozat NegyedikSorozat1 177 0 177
TetszőlegesSorozat1 147 1000 1147
Ötödik sorozat ÖtödikSorozat1 1084 0 1084
TetszőlegesSorozat1 147 1000 1147
Helyreállt tehát a világ rendje, miután a program kódjának és inputjának együttes hosszát tekintettük a
bonyolultság definíció alapjának. Azt vegyük észre, hogy nem bonyolultságról, hanem a bonyolultság felső
becsléséről beszélünk: azaz egy olyan számról, aminél a valódi bonyolultság kisebb, esetleg egyenlő lehet. Mert
ugye jöhet egy nálunk nagyobb hekker, aki még rövidebben megírja...
Miért jó nekünk ez a bonyolultság fogalom? Mert rá építve értelmesen tudjuk definiálni, hogy mit jelent véletlen
sorozatnak lenni. De a fenti Java nyelvű, intuitív megalapozás után mindenekelőtt pontosabban definiáljuk a
Chaitin-Kolmogorov bonyolultságot.
Egy x sorozat Chaitin-Kolmogorov bonyolultságának annak a Turing gépnek (T) és inputjának (y) az együttes
hosszát nevezzük, ami kiszámolja x-et az Univerzális Turing gépen és az ilyenek között a legrövidebb.
Itt a legrövidebb nem a futási időre vonatkozik, hanem, ahogy a Java példáknál is láttuk, a programok és az
input együttes hosszára. Egészen konkrétan ez annyit jelent, hogy megszámoljuk az U gép szalagján az
induláskor az input, azaz a T és az y 0 és 1 jegyekkel való megadása, lekódolása mennyi cellát foglal el.
A Chaitin-Kolmogorov bonyolultság egyébként programozóknak nagyon szemléletes: a vizsgált sorozat
bonyolultsága csakis kisebb vagy egyenlő lehet annak az algoritmusnak (Turing gépnek) és az algoritmus
bemenetének együttes hosszánál, amely algoritmus produkálni tudja ezt a sorozatot.
De micsoda fájdalom annak az Olvasónak, aki máris program írását tervezte arra, hogy kiszámolja a Chaitin-
Kolmogorov bonyolultságot, például az alábbi példa mintájára.
Mert fájdalom, de könnyen belátható - amit a következő példában meg is teszünk - hogy nem létezik olyan
Turing gép, ami ezt képes lenne megtenni, ebből már tudjuk, hogy nincs olyan algoritmus, ami képes lenne ezt
megtenni, tehát hasztalan lenne ilyen program írásával próbálkozni! (Nyilván ennek a lehetetlenségnek nincs
köze ahhoz, hogy a példa bemenet a Biblia volt :)
Figyelmeztetés a Javaban kezdő Olvasóknak
Page 45
A programozásról
17 Created by XMLmind XSL-FO Converter.
A következő állítás bizonyításaként szereplő Java programot csak fussa át a kezdő Olvasó, hogy szeme
szokja a Java nyelvet. Ha eközben a megértésben bármi zavar támadna - s a teljesen kezdőknek nyilván
támadni fog - akkor most bátran hagyja ki, s az utána szereplő ábrán győződjön meg az állítás
igazságáról.
1.8. példa - A Chaitin-Kolmogorov bonyolultság nem kiszámítható!
Programozóknak ez annyira fájdalmas megállapítás, hogy a kedves Olvasó meggyőzésére magunk is belátjuk
ezt az állítást, bizonyításaként megadjuk a [LOVÁSZ KÖNYV] ugyanezen bizonyítását megadó Pascal program
Java változatát.
Tegyük fel tehát, hogy van olyan algoritmus, ami kiszámolja a Chaitin-Kolmogorov bonyolultságot, ha ezt
beprogramozza az Olvasó, akkor a következő kódhoz hasonlót készít.
public class ChaitinKolmogorov {
/** A bonyolultság() függvényben lévő bekommentezett rész mérete. */
public static final int BONYOLULTSÁG_FGV_HOSSZA = 10000;
public static void main(String[] args) {
int i=0;
String sorozat = null;
while(true) {
sorozat = Integer.toBinaryString(i++);
if(bonyolultság(sorozat) > 10 * BONYOLULTSÁG_FGV_HOSSZA)
break;
}
System.out.println(sorozat);
}
public static long bonyolultság(String sorozat) {
long bonyolultság = 0;
/*
* Itt van lekódolva az az algoritmus, ami
* feltevésünk szerint létezik és kiszámítja
* a paraméterként kapott sorozat bonyolultságát.
* Ennek a résznek a hossza legyen mondjuk
* 10000, tízezer betű.
*/
return bonyolultság;
}
}
Page 46
A programozásról
18 Created by XMLmind XSL-FO Converter.
Ha meglenne a program, akkor fordítanánk, futtatnánk... Feltevésünk értelmében megvan, tehát képzeljük el,
hogy futtatjuk: amikor megáll, akkor kiír egy olyan szót, aminek a bonyolultsága > 100000, hiszen a main
függvény végtelen ciklusából akkor ugrunk ki a break utasítással, amikor ez a feltétel teljesül. De hiszen ez a
program input nélkül futott, mérete 10000+360 betű, a bonyolultság() függvényének bekommentezett része
helyetti valódi rész mérete + a maradék többi. Tehát 10360 karakteres programmal kiszámoltuk (kiírtuk) ezt a
sorozatot, ezért bonyolultsága ennél csak kisebb egyenlő lehet. Hoppá! Döbbenetes, nem igaz? Jó kis
ellentmondás, ami lehetetlenné teszi feltevésünk helyességét, tehát annak ellenkezője igaz, vagyis, hogy nem
létezik olyan algoritmus, amivel tetszőleges szóról kiszámolhatnánk a szó bonyolultságát.
1.9. példa - A Chaitin-Kolmogorov bonyolultság gyakorlati alkalmazásai
A Chaitin-Kolmogorov bonyolultság több nagyon érdekes gyakorlati alkalmazásáról olvashatunk például a
[VITÁNYI HASONLÓSÁG CIKK] cikkben. Itt a szerzők a Chaitin-Kolmogorov bonyolultságból egy távolság
fogalmat, a hasonlósági metrikát származtatják, aminek rögtön bemutatják két gyakorlati alkalmazását is. Az
első alkalmazás során 20 faj mitokondriális DNS-e alapján filogenetikai fát építenek, s a kapott eredményt a
hagyományos módszerek tükrében is bemutatják. A másikban 52 emberi nyelvet hasonlítanak össze és
ugyancsak bemutatják az eredményeket összefoglaló, a tárgyalt nyelvekre vonatkozó filogenetikai fát.
Érdekességként megjegyezhetjük, hogy vizsgált nyelvek között találjuk anyanyelvünket is. A cikkben
megvizsgált nyelvek összehasonlítását az ENSZ weblapján a szóban forgó nyelveken megtalálható Az Emberi
Jogok Egyetemes Nyilatkozatának fordításai alapján, ezeket a fordításokat korpuszonak tekintve végezték el.
A [HANGYÁK és KOLMOGOROV CIKK] cikkben a szerzők hangyákkal hajtanak végre egy kísérletet. Az
élelemhez vezető utat egy bináris fa formájában teszik lehetővé a hangyáknak, tehát a kísérleti elrendezésben a
hangyák nyelvén a LLRL azt jelenthetné, hogy először fordulj balra, majd megint balra, aztán jobbra végül balra,
hogy elérd a táplálékot. A gyakorló hangyászok megnyugtatására mondhatjuk, hogy a szerzők az egész kísérleti
berendezést egy vízzel teli kádba helyezték, így megakadályozták, hogy a hangyák levágják a kanyarokat. A
kísérleti eredmények mutatják, hogy a bonyolultabb utakról a felfedező hangyának több időre van szüksége,
hogy a többiekkel a táplálék forrás helyéről szóló, feltételezetten LLRL jellegű szerkezettel bíró információt
megossza.
A mobil játékfejlesztés egy formális modelljének megadására használják fel a bonyolultságot a [MOBIL JÁTÉK
ÉLMÉNY] cikkben. A szerzők által javasolt modellben a (számítógépes) játék egy a játék fejlesztője és a játék
felhasználója közötti kommunikációs csatorna. Grafikusan ezt a Shannon eredeti [SHANNON INFÓELM
CIKK] és Benczúr által némileg módosított [BENCZÚR CIKK] ismert séma alábbi átalakításával
szemléltethetjük:
Page 47
A programozásról
19 Created by XMLmind XSL-FO Converter.
A hivatkozott cikkben a szerzők definíciót adnak a jó játékra és megmutatják, hogy a jó játékok nyelve nem
rekurzív, azaz nincs olyan általános mechanisztikus eljárás, amivel el lehetne dönteni tetszőleges játékról, hogy
jó lesz-e.
1.1.1.4.1. 0, 1 feladatok
Figyelmeztetés a Javaban kezdő Olvasóknak
A következő Java nyelvű példák mindenféle egyszerű gyakorlatok a 0 és az 1 jegyekkel, bitekkel. Az
első átvált kettes számrendszerbe, a második egy bitműveleteket használó bináris dump (megmutatja a
bájtok bitjeit), a harmadik egy ugyancsak bitműveletes titkosító példa. A negyedik egy számrendszer
átváltásos példa. Az ötödik pedig egy Turing gépet kódol le egy 0 és 1 jegyekből álló sorozattá.
Ezen példák forrásait csupán fussa át a kezdő Olvasó, hogy szeme szokja a Java nyelvet. Ha eközben a
források megértésében bármi zavar támadna - és a teljesen kezdőknél nyilvánvalóan támadni fog -
akkor most bátran hagyja ki őket, s ha majd első labirintusos Java programjaink kipróbálásához érkezik
az anyag feldolgozásában, az után térjen vissza ide és írja meg (próbálja ki) ezeket a programokat.
Megjegyezhetjük, hogy ebben a Programozás papíron című részben általában is elegendő a szereplő
forrásokra csak egy pillantást vetni és inkább a programok használatát, működését megfigyelni: hogyan
indítjuk, milyen bemenettel fut, mit ír ki a képernyőre stb. Természetesen mindezt nem egy gép előtt
kipróbálva, hanem az általunk a kézikönyvbe illesztett néha Linuxos, néha Windowsos
pillanatfelvételeket megfigyelve.
1.10. példa - Átváltás binárisba
Már többször szóba került a 0, 1 jegyekkel való lekódolás. Lépjünk ebbe az irányba egy picikét! Lássuk azt a
Java programot, ami a bemenetét 0, 1 sorozattá alakítja. Ezt a programot a korábbi TetszőlegesSorozat1
program továbbfejlesztéseként fejlesszük ki! Következik néhány megoldás, a kezdő Olvasó ezeket, ahogy
megbeszéltük, rövid átfutás után most egyszerűen át is ugorhatja! (Mert ez nemhogy nem OO programozás
bevezető példa, de majd még bitfaragásba is fajul...)
public class NullaEgy {
public NullaEgy() {
int i = 0;
try {
while((i=System.in.read()) != -1)
System.out.printf("%s", Integer.toBinaryString(i));
} catch(java.io.IOException e) {
System.out.println("Hiba az olvasáskor, kilépek. " + e);
}
}
public static void main(String[] args) {
new NullaEgy();
}
}
Bájtonként olvassuk a program bemenetét, amit a Java programozó számára a System.in objektum reprezentál.
Ennek az objektumnak a read() módszere egy bájtot olvas be, azaz a beolvasás eredménye egy 00000000(bináris)
(0) és 11111111(bináris) (255) közé eső szám. Ha viszont már nem lehet mit olvasni a System.in bemenet
objektumról, akkor a read() függvénye ezt a -1 érték visszaadásával jelzi. Ezt azért tudja megtenni, mert a
Page 48
A programozásról
20 Created by XMLmind XSL-FO Converter.
read() függvény int típusú értéket ad vissza, ami nem csupán a 0-tól 255-ig tartó intervallumba eső számokat
képes hordozni, hanem ennél jóval-jóval nagyobb tartományt, a negatív -231-től (ami szokásosan kiírva -
2147483648) egészen a 231-1-ig (ami kiírva 2147483647).
A program while ciklusa törzsének egyetlen magányos utasítása nem csinál mást, mint egyszerűen kiírja a -1
számtól különböző beolvasott számot kettes számrendszerben az Integer osztály statikus
toBinaryString(egészet vár) bináris sztringet visszaadó (az iménti bizonyítás forrásában már használt)
függvényével.
De a bitfaragókat ez a program nem elégíti ki, több okból sem: a kapott 0,1 bináris sorozatot nem lehet
visszaalakítani - meg tudja mondani a kedves Olvasó, hogy miért nem? Ha nem, akkor próbálja megtenni
mondjuk a 012abc betűket tartalmazó bemenet.txt állománnyal! A bemenetet triviálisan úgy tudjuk megadni,
hogy kiadjuk a java NullaEgy parancsot, ami elindítja Java programunkat, majd begépeljük a 012abc inputot és
entert nyomunk. De ennél jóval elegánsabb, ha a bemenet.txt állomány tartalmát parancssorból küldjük
programunk bemenetére. Két módot mutatunk erre, az elsőben a < jel szemléletesen mutatja az irányítást. Az
Olvasó, ha gép előtt ülne, akkor (a fenti tartalmú NullaEgy.java forrásállomány létrehozása, majd lefordítása
után) ezt láthatná egy parancsablakában:
C:\...> java NullaEgy < bemenet.txt
110000110001110010110000111000101100011
másik lehetőség a | (csővezeték jel) használata, ami a bal oldalán lévő parancs kimenetét a jobb oldalán lévő
bemenetére (cső)vezeti:
C:\...> type bemenet.txt|java NullaEgy
110000110001110010110000111000101100011
Most a lényeghez visszatérve: ha 8 bitenként vissza akarjuk váltani a kapott
110000110001110010110000111000101100011 kimenetet, akkor gondban vagyunk, mert ez csak 39 jegy,
nekünk pedig az átváltáshoz 6x8 jegy kellene. A problémát az okozta, hogy az átváltott bináris számok nem
voltak 8 jeggyel kiírva, hanem mindig csak annyival, amekkora az adott szám volt:
0 48 110000
1 49 110001
2 50 110010
a 97 1100001
b 98 1100010
c 99 1100011
ezért nem tudnánk ezt a kódolást dekódolni, mert honnan tudnánk, hogy az elejétől elindulva éppen 6, 7 vagy
nyolc jegyenként kéne visszaváltani a bitmintákat?
A másik ok, ami miatt a bitfaragókat nem elégíti ki a tárgyalt program, hogy az Integer osztály statikus
metódusának hívásával túl egyszerűen jutnak a bináris számhoz, ezért írnak egy jobbat bitműveletekkel...
Figyelmeztetés a Javaban kezdő Olvasóknak
Ha az előző forrást átugrottuk, akkor a most következő NullaEgyDump forrás kihagyása még
indokoltabb.
Aki viszont már nem kezdő és bele akar gabalyodni, annak is csak annyi iránymutatást adunk, hogy az
egye változóba a 00000000000000000000000010000000 mintát az éppen vizsgált bájttal beéselve
Page 49
A programozásról
21 Created by XMLmind XSL-FO Converter.
tesszük, aminek eredménye a 00000000000000000000000000000000, ha a vizsgált bájt balról első
bitje 0 és 00000000000000000000000010000000, ha a vizsgált bájt balról első bitje 1. Aztán az egye
változó bitmintáját jobbra tologatjuk, a vizsgált bájt bitjeit pedig balra...
1.11. példa - Bitműveletek, kezdjük egy bináris dumppal
Tehát ott tartottunk, hogy írunk egy jobbat bitműveletekkel!
public class NullaEgyDump {
public NullaEgyDump() {
try {
byte [] buffer = new byte[1];
while(System.in.read(buffer) != -1) {
for(int i=0; i<8; ++i) {
int egye = buffer[0] & 0x00000080;
if((egye>>>7) == 1)
System.out.print(1);
else
System.out.print(0);
buffer[0] <<= 1;
}
}
} catch(java.io.IOException e) {
System.out.println("Hiba az olvasáskor, kilépek. " + e);
}
}
public static void main(String[] args) {
new NullaEgyDump();
}
}
Ez már egy igazi dump jellegű program lett: a bemenetét bájtonként veszi és kiírja a bájt nyolc bitjét. Próbáljuk
ki így:
C:\...> java NullaEgyDump < NullaEgyDump.class
11001010111111101011101010111110000000000000000000000000001100100000000001000001
00001010000000000001000000000000000111010000100100000000000111100000000000011111
00001010000000000010000000000000001000010000100100000000000111100000000000100010
...
azaz beadva neki inputként a saját class állományát! Ellenőrizzük, hogy jó-e a kimenet! Az első 4x2x4 jegyet,
ami a 11001010111111101011101010111110, tegyük át hexába, ha az eredmény nem ugyanaz, mint a
Bitfaragó feladat című feladat megoldása, akkor „még van kanál” [MÁTRIX MOZI], azaz még dolgozzunk a
megoldáson!
Page 50
A programozásról
22 Created by XMLmind XSL-FO Converter.
Az utolsó 0,1 feladatként megírunk majd egy olyan programot, ami egy Turing gépet alakít át egy bináris szóvá,
de előtte időzzünk még kicsit a bitműveleteknél, a következő klasszikus példánál!
1.12. példa - Titkosítás kizáró vaggyal
„...ez a módszer az egyik legrégibb és legismertebb. Hacsak a kulcs nem nagyon hosszú, a
CIA vagy az NSA várhatóan egy napon belül megfejti file-unkat.” —Kernighan-Plauger A programozás magasiskolája
A kizáró vagyos titkosítás során a titkosítandó szöveg bájtjait lefedjük a titkosító kulcs bájtjaival és az egymás
alá eső biteken végrehajtunk egy kizáró vagy műveletet. A kizáró vagy 1 értéket ad, ha a két bit különböző és 0
értéket, ha megegyező.
11001011 - a tiszta szöveg egy bájtja
^10101100 - a kulcs bájtja
---------
01100111 - kódolt szöveg, amit
^10101100 - újra beexorozzuk kulccsal
---------
11001011 - az eredeti tiszta szöveg
a [KERNIGHAN PROG KÖNYV]-ben részletesen ismertetett eljárás érdekessége, hogy a titkosítás újbóli
végrehajtásával visszakapjuk az eredeti szöveget. Mi ezt az algoritmust az ExorTitkosító osztályban
valósítottuk meg. S alább bemutatjuk ennek a programnak egy felhasználását. Ha az Olvasó lefordítaná, majd
elindítaná az alma parancssor-argumentummal, a program kimenetét pedig átirányítaná a titkosított.szöveg
állományba, miközben a program indulása után annak bemenetére gépelné be a titkosítandó
Ez titkosítva lesz!
Titkosítva, bizony!
szöveget, akkor ezt látná:
[norbi@niobe ~]$ javac ExorTitkosító.java
[norbi@niobe ~]$ java ExorTitkosító alma > titkosított.szöveg
Ez titkosítva lesz!
Titkosítva, bizony!
[norbi@niobe ~]$ more titkosított.szöveg
$LLk5
AALk
[norbi@niobe ~]$ java ExorTitkosító alma < titkosított.szöveg
Ez titkosítva lesz!
Titkosítva, bizony!
Page 51
A programozásról
23 Created by XMLmind XSL-FO Converter.
A Windows parancssorból dolgozóknak
A Windows parancssorból dolgozó kedves Olvasó teljesen a Linux esetén mutatottak mintájára járhat
el:
C:\...> javac ExorTitkosító.java
C:\...> java ExorTitkosító alma > titkosított.szöveg
Ez titkos lesz!
C:\...> type titkosított.szöveg
...olvashatatlan...Llk
C:\...> java ExorTitkosító alma < titkosított.szöveg
Ez titkos lesz!
C:\...>
A titkosított szöveg láthatóan emberi fogyasztásra alkalmatlan. A dekódoláshoz nem kell mást tennie az
Olvasónak, mint újra futtatni a programot ugyanazzal a kulccsal, de most bemenetként az iménti futtatáskor
elkészített titkosított szöveget beleirányítva (java ExorTitkosító alma < titkosított.szöveg) . Az
eredmény a sztenderd kimeneten jelentkezik, azaz, amint fent látta az Olvasó, a képernyőn látható.
public class ExorTitkosító {
public ExorTitkosító(String kulcsSzöveg,
java.io.InputStream bejövőCsatorna,
java.io.OutputStream kimenőCsatorna)
throws java.io.IOException {
byte [] kulcs = kulcsSzöveg.getBytes();
byte [] buffer = new byte[256];
int kulcsIndex = 0;
int olvasottBájtok = 0;
while((olvasottBájtok =
bejövőCsatorna.read(buffer)) != -1) {
for(int i=0; i<olvasottBájtok; ++i) {
buffer[i] = (byte)(buffer[i] ^ kulcs[kulcsIndex]);
kulcsIndex = (kulcsIndex+1) % kulcs.length;
}
kimenőCsatorna.write(buffer, 0, olvasottBájtok);
}
}
public static void main(String[] args) {
try {
new ExorTitkosító(args[0], System.in, System.out);
} catch(java.io.IOException e) {
e.printStackTrace();
}
}
}
Page 52
A programozásról
24 Created by XMLmind XSL-FO Converter.
A külső while ciklus buffer tömbönként addig olvassa a bemenetet, amíg csak tudja. A belső for ciklusban
helyezzük rá a kulcsot a beolvasott bájtokra a kulcsIndex változó segítségével, majd végrehajtjuk a kizáró
vagy műveletet, az eredmény a buffer tömbben keletkezik, amit végül a kimenetre írunk.
while((olvasottBájtok =
bejövőCsatorna.read(buffer)) != -1) {
for(int i=0; i<olvasottBájtok; ++i) {
buffer[i] = (byte)(buffer[i] ^ kulcs[kulcsIndex]);
kulcsIndex = (kulcsIndex+1) % kulcs.length;
}
kimenőCsatorna.write(buffer, 0, olvasottBájtok);
}
1.13. példa - Kizáró vagyos titkosítás grafikusan
Az iménti példát lássuk el grafikus felülettel! Egy ilyen felület kinézetére láthatunk példát a Jávácska portál
Jávácska titkosítójánál. Jelen saját megoldásunkat mi a Bepillantás a GUI programozásba című fejezetünkben
adjuk meg. A példa érdekessége, hogy Swinges grafikus felületet és legördülő menüt használ.
1.14. példa - Számrendszer átváltások
A következő TörtÁtváltó osztály törteket vált át valamilyen számrendszerbe. Például megmondja, hogy a tízes
számrendszerbeli 5*16-1 + 0*16-2 + 15*16-3 = 0,316162109375 hexában 0.50F amint ezt a következő, a
tízesből16osba() függvényben beprogramozott algoritmus - szorzok 16-tal, az eredmény egész része a hexa
számjegy, az eredmény törtrészét megint szorzom és így tovább, amíg a törtrész el nem fogy - adja:
0.316162109375 * 16 = 5.05859375
0.05859375 * 16 = 0.9375
0.9375 * 16 = 15.0
Az egész részek a helyes sorrendben keletkeznek, leolvasva: 50F, tehát 0,316162109375decimális = 0.50Fhexadecimális
public class TörtÁtváltó {
public static String tízesből16osba(double tört) {
StringBuffer sb = new StringBuffer();
int számrendszer = 16;
Character hexaJegyek[] = {'A', 'B', 'C', 'D', 'E', 'F'};
while(tört != 0.0d) {
int jegy = (int) Math.floor(számrendszer*tört);
if(jegy<10)
sb.append(jegy);
else
sb.append(hexaJegyek[jegy-10]);
Page 53
A programozásról
25 Created by XMLmind XSL-FO Converter.
tört = (számrendszer*tört) - Math.floor(számrendszer*tört);
}
return sb.toString();
}
public static void main(String[] args) {
double tört =
+ 0 * (1.0/16.0)
+ 15 * (1.0/(16.0*16.0))
+ 2 * (1.0/(16.0*16.0*16.0))
+ 10 * (1.0/(16.0*16.0*16.0*16.0))
+ 5 * (1.0/(16.0*16.0*16.0*16.0*16.0));
System.out.println(tört);
System.out.println(TörtÁtváltó.tízesből16osba(tört));
System.out.println(TörtÁtváltó.tízesbőlKettesbe(tört));
}
}
Ha a kedves Olvasó fordítaná és futtatná a programot, akkor a következőket látná:
C:\...> javac TörtÁtváltó.java
C:\...> java TörtÁtváltó
0.05923938751220703
0F2A5
A tízesből16osba() függvényben használt kódot a A Pi jegyeinek nyomában című pont PiBBP osztályában
használjuk majd fel, ahol a Pi hexadecimális törtkifejtését állítjuk elő.
1.15. példa - A TörtÁtváltó osztály kiegészítése
Az iménti TörtÁtváltó osztályhoz írjunk egy tízesbőlKettesbe() függvényt!
public static String tízesbőlKettesbe(double tört) {
StringBuffer sb = new StringBuffer();
int számrendszer = 2;
while(tört != 0.0d) {
int jegy = (int) Math.floor(számrendszer*tört);
if(jegy<10)
sb.append(jegy);
tört = (számrendszer*tört) - Math.floor(számrendszer*tört);
}
return sb.toString();
}
A bővítést elvégezve újrafordítás és futtatás után ezt látná az Olvasó:
C:\...> javac TörtÁtváltó.java
Page 54
A programozásról
26 Created by XMLmind XSL-FO Converter.
C:\...> java TörtÁtváltó
0.05923938751220703
0F2A5
00001111001010100101
ellenőrizve: 4 bitenként binárisból hexába váltva valóban 0000 = 0, 1111 = F, 0010 = 2, 1010 = A, 0101 = 5.
1.16. példa - Turing gépek kódolása
Például az [ALGORITMUSOK KÖNYV] 206. oldalán javasolt kódolás alapján készítsünk egy olyan
programot, ami egy Turing gépet 0 és 1 jegyekből álló sorozattá alakít!
Barangoljunk a véletlen 0 és 1 jegyek birodalmában! A következő pontban véletlen sorozatokkal találkozunk, a
reá következőben pedig leleplezzük, hogy ezek mégsem véletlenek!
1.1.1.4.2. Véletlen 0, 1 sorozatok
A következő program előállít egy véges véletlen sorozatot, egészen pontosan példányosít egy Random
objektumot, majd egy ciklusban elkér tőle 1000 darab 0 vagy 1 számot, amiket egyébként a program a
generálásuk után azonnal ki is ír a sztenderd kimenetére.
public class VéletlenSorozat {
public static void main(String[] args) {
java.util.Random generátor = new java.util.Random(42);
for(int i=0; i<1000; ++i)
System.out.print(generátor.nextInt(2));
}
}
Futtassuk le kétszer egymás után a programot, a kimeneteket irányítsuk át az első.sorozat és a
második.sorozat állományba, majd hasonlítsuk össze, hogy a két állomány különbözik-e:
[norbi@niobe ~]$ javac VéletlenSorozat.java
[norbi@niobe ~]$ java VéletlenSorozat > első.sorozat
[norbi@niobe ~]$ java VéletlenSorozat > második.sorozat
[norbi@niobe ~]$ diff első.sorozat második.sorozat
[norbi@niobe ~]$
láthatóan a diff program nem jelentette (lefutott és üzenet nélkül visszaadta a promptot), hogy különböznek,
tehát megegyeznek. Ez meglepő! Lehet ugyanaz az eredmény a második futásnál, ha a sorozat véletlen? Igen,
mert ezek a sorozatok nem valódi véletlenek, hanem egy algoritmus produkálta számok. Ezeknek az
algoritmusoknak (kongruencia generátoroknak) fontos tulajdonsága, hogy egy idő után elkezdik ismételni a
generált számokat. Minél nagyobb ez az idő, annál jobbnak tartjuk a generáló algoritmust.
A Random objektum készítésekor átadott 42 szám azt mondja meg, hogy a generáló algoritmus milyen állapotból
induljon. Mivel mindkét futásnál a 42 szerepelt, így már nem meglepő, hogy mindkét futásra ugyanazt a
számsorozatot generálta a programunk.
1.17. példa - Kongruencia generátorok
Page 55
A programozásról
27 Created by XMLmind XSL-FO Converter.
Az xt+1 = a*xt + b (mod m) sorozatot kongruencia generátornak nevezzük. Az y mod m y maradékos osztását
jelenti az m értékkel, azaz y % m. Látványosan úgy értelmezhetjük, hogy az y értéket le kell tekergetnünk egy m
órabeosztást tartalmazó csak kismutatós órán és az eredmény az, ahol a mutató megállt. Például 49 mod 12 = 1.
Mennyi a periódusa a Az xt+1 = 7*xt + 9 (mod 11) generátornak például a 42 értékkel indítva?
Ha a kedves Olvasó nem akarja papírral, ceruzával számolgatni, akkor a következő kis programot írná meg:
public class KongruenciaGenerátor {
int aktuális;
int a = 7;
int b = 9;
int m = 11;
public KongruenciaGenerátor(int első) {
aktuális = első;
}
public int következő() {
aktuális = (a*aktuális + b) % m;
return aktuális;
}
public static void main(String[] args) {
KongruenciaGenerátor g = new KongruenciaGenerátor(42);
for(int i=0; i<100; ++i) {
System.out.print(g.következő());
System.out.print(" ");
}
}
}
amit ha lefordítana és futtatna, akkor az alábbi eredményeket kapná:
C:\...>javac KongruenciaGenerátor.java
C:\...>java KongruenciaGenerátor
6 7 3 8 10 2 1 5 0 9 6 7 3 8 10 2 1 5 0 9 6 7 3 8 10 2 1 5 0 9 6 7 3 8 10 2 1 5
0 9 6 7 3 8 10 2 1 5 0 9 6 7 3 8 10 2 1 5 0 9 6 7 3 8 10 2 1 5 0 9 6 7 3 8 10 2
1 5 0 9 6 7 3 8 10 2 1 5 0 9 6 7 3 8 10 2 1 5 0 9
ahonnan látható, hogy a periódus az alábbi
6 7 3 8 10 2 1 5 0 9
hossza tehát 10.
Magában a JDK-ban is kongruencia generátort építettek be az egyenletes eloszlású számok generálására. A JDK
sikeres telepítés után a JDK könyvtárában találhatjuk az src.zip nevű állományt, amiben megtaláljuk a Java
SE OO világ összes osztályának forráskódját, így a Random osztályét is. Itt a java/util/Random.java forrást
Page 56
A programozásról
28 Created by XMLmind XSL-FO Converter.
kinyitva nézzük meg, hogy milyen értékkel lesz inicializálva a generátor, azaz honnan lesz indítva, ha a Random
osztály paraméter nélküli konstruktorát hívjuk, azaz new java.util.Random() formában példányosítunk.
Tanács a Javaban nem kezdő és türelmetlen Olvasóknak
Ha a kedves Olvasó azonnal meg akarja válaszolni az iménti kérdést és ennek megfelelően azonnal
telepíteni akarja a JDK-t, azaz a Java fejlesztői környezetet a gépére, akkor A Java telepítése gépünkre
című pontban talál segítséget.
1.18. példa - Galton deszka kísérlet
Látványos feladat olyan grafikus szimulációs programot írni, mely demonstrálja a például a [RÉNYI VALSÉG
KÖNYV] könyvben leírt Galton deszkás kísérletet. A függelékben ezt a Galton deszka kísérlet programja című
pontban mi is megtesszük majd, most csupán a kísérletet ismertetjük és egy előzetes pillantást vetünk a
programunkkal szimulált két különböző Galton utáni kísérleti elrendezésre.
A kísérleti elrendezésben deszkasorokat készítünk. Az első sorba egy deszkát, a másodikba kettőt, a harmadikba
hármat, s így tovább teszünk. A deszkákat pontosan úgy helyezzük el, hogy a felülről ráeső golyó 50-50
százalék eséllyel essen tovább a deszka bal vagy jobb oldalán, az alatta lévő következő deszka sorra. A kísérlet
során arra vagyunk kíváncsiak, hogy a berendezésbe beejtett golyók a sorokon átesve hová érkeznek?
Page 57
A programozásról
29 Created by XMLmind XSL-FO Converter.
A zöld oszlopokkal kirajzolt hisztogram mutatja, hogy az adott helyre mennyi golyó esett a kísérlet végrehajtása
során. Mi most empirikusan mutattuk meg, a hivatkozott [RÉNYI VALSÉG KÖNYV] könyvben pedig
teoretikusan mutatja meg a szerző, hogy határátmenetben, azaz ha végtelen sok golyót ejtenénk be a kísérleti
elrendezésbe, akkor a kialakuló hisztogram alakja a híres Gauss-féle harang görbe alakját rajzolná ki. A
véletlennel kapcsolatos jelenségeknél a harang görbe megjelenése a normális eloszlás felbukkanására utal.
A normális eloszlásnál jegyezhetjük meg, hogy matematika szakos hallgatóknak/tanároknak remek OO
bevezető példa lehet egy alább itt is bemutatásra kerülő polártranszformációs normális generátor megírása
Javaban. Miért? Mert programjuk megírása után a JDK fent említett src.zip állományában a
java/util/Random.java forrásban megnézhetik, hogy magában a JDK-ban is hasonlóan oldották meg a
feladatot a Sun programozói! Ezért ennek a generátornak a megírását magunk is megtesszük most a következő
pontban.
A [VÉLETLEN KÖNYV] 98. oldala vagy a [KNUTH 2. KÖNYV] 128. oldala alapján készítsük el a módosított
polármódszeres algoritmust Javaban, avagy - Javaban kezdőként - csupán figyeljük meg az elkészítését! Az
algoritmus matematikai háttere most számunkra lényegtelen, fontos viszont az eljárás azon jellemzője, hogy egy
számítási lépés két normális eloszlású számot állít elő, tehát minden páratlanadik meghíváskor nem kell
számolnunk, csupán az előző lépés másik számát visszaadnunk. Hogy páros vagy páratlan lépésben hívtuk-e
meg a megfelelő számítást elvégző következő() függvényt, a nincsTárolt logikai változóval jelöljük. Igaz
értéke azt jelenti, hogy tárolt lebegőpontos változóban el van tárolva a visszaadandó szám. (Jelen
tárgyalásunkban a következő() függvényben implementált matematikai eljárás, a módosított polármódszer
további tulajdonságai teljesen érdektelenek.)
public class PolárGenerátor {
boolean nincsTárolt = true;
double tárolt;
Page 58
A programozásról
30 Created by XMLmind XSL-FO Converter.
public PolárGenerátor() {
nincsTárolt = true;
}
public double következő() {
if(nincsTárolt) {
double u1, u2, v1, v2, w;
do {
u1 = Math.random();
u2 = Math.random();
v1 = 2*u1 - 1;
v2 = 2*u2 - 1;
w = v1*v1 + v2*v2;
} while(w > 1);
double r = Math.sqrt((-2*Math.log(w))/w);
tárolt = r*v2;
nincsTárolt = !nincsTárolt;
return r*v1;
} else {
nincsTárolt = !nincsTárolt;
return tárolt;
}
}
public static void main(String[] args) {
PolárGenerátor g = new PolárGenerátor();
for(int i=0; i<10; ++i)
System.out.println(g.következő());
}
}
Most osztályunkat összevethetjük a JDK fent említett src.zip állományában a java/util/Random.java
forrásban implementált megoldással, ahol a nextGaussian() függvény törzsét és a kapcsolódó (felette
definiált) haveNextNextGaussian és nextNextGaussian példánytagokat tanulmányozzuk!
Ha az Olvasó a programot lefordítaná és futtatná, akkor például a következő kimenetet kaphatná:
-0.7302435745349951
0.3398973333782606
-0.1745186408410782
-0.6733291138289893
-0.7141255333702377
0.8105205642319349
-0.2166963741203095
-0.6100935726625737
-0.0061257158500475665
0.09213084665478943
Page 59
A programozásról
31 Created by XMLmind XSL-FO Converter.
Figyelmeztetés a Javaban kezdő Olvasóknak
A következő feladat forrásában a Javaban kezdő kedves Olvasóknak elég a /** és */ közötti
dokumentációs és a /* */ közötti, vagy a // mögötti megjegyzéseket átolvasnia.
1.19. példa - Hisztogram feladat
Az, hogy a generált számok normális eloszlásúak, szemléletesen annyit tesz, hogy a számokból épített
hisztogram a haranggörbe alakjához hasonlatos. A hisztogram oszlopok olyan rendszere, ahol egy oszlop
magassága azt mutatja, hogy az oszlop szélességébe a vizsgált számok közül mennyi esett. Például az imént
generált 10 szám a -1, 0 és a 0, 1 intervallumok felett elhelyezkedő egységnyi széles két doboz között úgy oszlik
meg, hogy az elsőbe 7, a másodikba 3 szám esik, az ennek megfelelő hisztogram grafikonja a következő alakú.
A következő ábrán 100000 generált normális szám egy hisztogramját mutatjuk be.
Vessünk egy pillantást a kézikönyvnek arra az osztályára, mellyel a szereplő hisztogram képeket készítettük,
próbáljuk meg végigolvasni a forráskódot! De közben vigyázzunk, hogy most még ne bonyolódjunk a részletek
boncolgatásába! Szorítkozzunk inkább a dokumentációs megjegyzések értelmezésére, semmint az implementált
függvények törzsének részletes vizsgálatára.
public class Hisztogram {
/** A feldolgozandó értékek alsó határa. */
protected double min;
/** A feldolgozandó értékek felső határa. */
protected double max;
/** A hisztogram dobozai. */
protected int [] dobozok;
/**
* Létrehoz egy <code>Hisztogram</code> objektumot.
*
Page 60
A programozásról
32 Created by XMLmind XSL-FO Converter.
* @param min a feldolgozandó értékek alsó határa.
* @param max a feldolgozandó értékek felső határa.
* @param méret a hisztogram dobozainak száma.
*/
public Hisztogram(double min, double max, int méret) {
// Példánytagok aktualizálása:
this.min = min;
this.max = max;
// Létrehozzuk a megfelelő méretű hisztogrammot.
dobozok = new int[méret];
// Paranoiás stílus: végigzongorázzuk a tömb elemeit
for(int i=0; i<dobozok.length; ++i)
// és nullázzuk a hisztogram dobozainak tartalmat:
dobozok[i] = 0;
}
/**
* A kapott érték melyik dobozba esik?
*
* <pre>
* 0. 1. dobozok.length-1
* ---------|-----|-----|-----|-----|-----|-----|-----------
* min | max
* érték
* </pre>
*
* @param érték a hisztogram megfelelő dobozába
*/
public void betesz(double érték) {
++dobozok[(int)((érték-min)/((max-min)/dobozok.length))];
}
/**
* Kirajzolja a hisztogrammot.
*
* @return BufferedImage a hisztogram grafikonjának képe.
*/
public java.awt.image.BufferedImage grafikon() {
int képSzélesség = 300;
int dobozSzélesség = 300/dobozok.length;
int maxDobozÉrték = 0;
for(int i=0; i<dobozok.length; ++i)
if(dobozok[i] > maxDobozÉrték)
maxDobozÉrték = dobozok[i];
int képMagasság = 300;
java.awt.image.BufferedImage grafikon =
new java.awt.image.BufferedImage(
képSzélesség,
képMagasság,
java.awt.image.BufferedImage.TYPE_INT_RGB
);
java.awt.Graphics g = grafikon.getGraphics();
// Kép törlése: fehérrel
g.setColor(java.awt.Color.WHITE);
// lemeszelünk egy képernyőnyi méretű téglalapot:
g.fillRect(0, 0, grafikon.getWidth(), grafikon.getHeight());
for(int i=0; i<dobozok.length; ++i) {
// Egy doboz kirajzolása
g.setColor(java.awt.Color.YELLOW);
if(maxDobozÉrték/képMagasság != 0)
g.fillRect(i*dobozSzélesség,
képMagasság-dobozok[i]/(maxDobozÉrték/képMagasság),
képSzélesség/dobozok.length,
dobozok[i]/(maxDobozÉrték/képMagasság));
Page 61
A programozásról
33 Created by XMLmind XSL-FO Converter.
else
g.fillRect(i*dobozSzélesség,
képMagasság-dobozok[i]*(képMagasság/maxDobozÉrték),
képSzélesség/dobozok.length,
dobozok[i]*(képMagasság/maxDobozÉrték));
// Esztétikus a doboz bekeretezése
g.setColor(java.awt.Color.LIGHT_GRAY);
if(maxDobozÉrték/képMagasság != 0)
g.drawRect(i*dobozSzélesség,
képMagasság-dobozok[i]/(maxDobozÉrték/képMagasság),
képSzélesség/dobozok.length,
dobozok[i]/(maxDobozÉrték/képMagasság));
else
g.drawRect(i*dobozSzélesség,
képMagasság-dobozok[i]*(képMagasság/maxDobozÉrték),
képSzélesség/dobozok.length,
dobozok[i]*(képMagasság/maxDobozÉrték));
}
g.dispose();
return grafikon;
}
/**
* Példányosít egy hisztogram objektumot, akinek továbbítja a
* bemenetről olvasott számokat, végül kimenti a hisztogramot.
*/
public static void main(String[] args) throws Exception {
// Feltesszük, hogy korábban már megállapítottuk
// a bejövő számok minimumát és maximumát, ezek
// a most csak becsült -4.5, 4.5
Hisztogram h = new Hisztogram(-4.5, 4.5, 40);
java.io.BufferedReader inputCsatorna =
new java.io.BufferedReader(
new java.io.InputStreamReader(System.in));
String sor = null;
// Olvassuk a bemenetet, amíg tudjuk
while((sor = inputCsatorna.readLine()) != null)
// az olvasott számot továbbitjuk a
// hisztogram objektumnak:
h.betesz(Double.parseDouble(sor));
// A számok feldolgozása után egy képállományba írjuk
// ki az elkészített hisztorgramm grafikonját:
javax.imageio.ImageIO.write(h.grafikon(), "png",
new java.io.File("hisztogram.png"));
}
}
Ha az Olvasó módosítaná az előző pont PolárGenerátor osztályának indító main() módszerében a ciklust,
hogy 100000 normálist állítson elő, s az osztály fordítása után kimenetét a 100000.normális állományba
irányítaná, amit pedig inputként irányítana a futtatott Hisztogram objektumba:
C:\...> java PolárGenerátor >100000.normális
C:\...> java Hisztogram < 100000.normális
akkor a példa bevezetésében mutatott ábra haranggörbe hisztogramját kapná a program által generált a
hisztogram.png állományban.
Feladat a Javaban nem kezdő Olvasóknak
Page 62
A programozásról
34 Created by XMLmind XSL-FO Converter.
Az iménti Hisztogram osztály betesz(double érték) módszerét módosítsuk úgy, hogy dobjon egy
RosszÉrtékException saját kivétel objektumot, ha az aktuális paraméterként kapott érték kisebb,
mint a hisztogram min vagy nagyobb, mint a max értéke. A kivétel objektumba üzenetként csomagoljuk
be, hogy éppen melyik szituáció következett be.
A Javaban kezdő Olvasó akkor készítse el ezt a feladatot, ha a kapcsolódó kivételkezelési bevezető
részeket már feldolgozta!
1.20. példa - Fej vagy írás varázslat
A [VÉLETLEN VÉLETLEN] könyvben javasolt játék a véletlennel a következő: a tanulók egy részétől azt
kérjük, hogy írjanak papírra egy 200 elemből álló 0, 1 sorozatot úgy, hogy a sorozat minden tagját egy érme
feldobásával állítják elő. A tanulók másik csoportja ugyancsak 200 elemű 0, 1 sorozatot készít, de ők fejből
írják. S csodák csodája a tanár megtekintve a sorozatokat - kis hibával - megmondja, hogy mely sorozatok
alapulnak valódi pénzfeldobáson. Módszere, hogy azokról a sorozatokról mondja ezt, melyekben van egymást
követő legalább 6 darab nulla vagy egy, mert a fejből generáló tanuló ennyi azonos jegyet egymás után már nem
tekint véletlennek.
Íme egy példa a pénzérmével való dobás alapján képzett sorozatra:
11110010010010111101001110010001110011101100111100
00110011001101110111000101001111110001001001001100
10100111011100100111000100000011101101001000010011
11011000011111000101101010001001111010010011000010
S egy másik a fejből véletlennek generált/titulált alapon képzett sorozatra:
01110010110100110101000010110010110100101011110101
10011001010101101001001000110101010100100101101001
11001100011001111011011110100010110010111010100101
10000110101110110100001101010110111010101101010011
Az eddigi és még néhány elkövetkező, algoritmuselméleti ihletésű pontokat a „Nem fontos a pontosság, csak azt
tudjuk, hogyan lehet elérni” - ezt a tanácsot adta számtalanszor egyetemünk logika vagy mesterséges
intelligencia kurzusain hallgatóinak Dragálin Albert professzor úr - gondolat jegyében bontottuk ki. Azoknak,
akik az érintett alapfogalmak teljesen pontos tárgyalását keresik a [ALGORITMUSOK KÖNYV] vagy a
[LOVÁSZ KÖNYV] könyveket ajánljuk.
1.1.1.5. Tényleg véletlen 0, 1 sorozatok
Vizsgálódjunk kicsit tovább az 1000 bit hosszúságú bináris szavak között! Egy bites szóból 2 van, a 0 és az 1.
Két bites szóból 2x2 darab, a 00, 01, 10, 11. Három bites szóból 2x2x2, 28, négy bitesből 2x2x2x2, 24, ...
láthatóan végül 1000 bites szóból 21000 darab van; ez jó sok, jóval több, mint búzaszem a sakktáblán.
Vajon mennyi 1000 bites szó Chaitin-Kolmogorov bonyolultsága kisebb 990-nél és mennyié nagyobb?
A 990-nél kisebb bonyolultságúak, a korábban kimondott definíciónk alapján, azok a szavak lehetnek, melyeket
990-nél rövidebb bemenetből ki tudunk számítani egy univerzális Turing géppel
ahol a T algoritmus és i inputja együttes hossza kisebb 990-
nél. Könnyen, erősen felülről tudjuk becsülni az ilyen 990 bitnél rövidebb univerzális Turing gép bemeneteket.
Mert 990-nél rövidebb bitminta összesen lehet 2 darab 1 bites + 4 darab 2 bites + 8 darab 3 bites + ... + 2989
Page 63
A programozásról
35 Created by XMLmind XSL-FO Converter.
darab 989 bites = 2990 -1 darab összesen. A -1 bemenetet még nagylelkűen hozzácsapva, számoljunk 2990
darabbal.
Tehát maximum 2990 darab szó lehet 990-nél egyszerűbb és ennek megfelelően 21000 / 2990 = 1024, azaz körülbelül
maximum 1000-szer több olyan szó van, ami pedig 990-nél bonyolultabb. Összefoglalva az 1000 bit hosszúságú
bináris szavak minimum 99.9 százaléka 990-nél nagyobb bonyolultságú!
Ugyanezzel a gondolatmenettel láthatnánk be, hogy az 1000 bit hosszúságú szavak között a 999-nél egyszerűbb
szavak maximum 21000-1-en vannak, tehát legalább egy szónak lennie kell, ami bonyolultsága >= 1000.
1.1.1.5.1. Végtelen és véletlen 0, 1 sorozatok
Ebben a pontban megmutatjuk, hogy a megismert Chaitin-Kolmogorov bonyolultság fogalmunkkal miként
tudjuk úgy definiálni a végtelen véletlen sorozat fogalmát, hogy intuíciónk közben meg ne hökkenne. Mert meg
lenne hökkenve? Döntse el önmaga a kedves Olvasó, gondoljon arra, hogy betesszük egy urnába az összes 1000
bit hosszúságú 0, 1 sorozatot. Ekkor a korábbi A Chaitin-Kolmogorov bonyolultság című pont második sorozatát
kihúzni ugyanolyan valószínű, mint az ötödiket, mert mindkét esetben arról van szó, hogy a kihúzott sorozat
bitjeinek az egyik esetben pont annak kell lenni, mint a második vagy a másik esetben pont annak, mint az
ötödik sorozatban a biteknek. Ez a nézőpont szemléletünkkel jól megfér. De mindemellett az ötödik sorozatot
természetesen véletlennek tekintjük, míg az másodikról lerí, hogy teljesen triviálisan szabályos, hogy is lehetne
hát véletlen! - mondja a szemléletünk. A két szemléletében ellentétes megközelítés szülheti meg
meghökkenésünket.
1.21. példa - Egy szabályos sorozat
Készítsünk egy olyan programot, ami bemenetén kapott n számig kiírja a A Chaitin-Kolmogorov bonyolultság
című pont második sorozatát!
public class BinárisSorozat {
public static void main(String[] args) {
int db = 0;
try {
db = Integer.parseInt(args[0]);
} catch(NumberFormatException e) {
db = 100;
}
Page 64
A programozásról
36 Created by XMLmind XSL-FO Converter.
for(int i=0; i<db; ++i)
System.out.print(i%2);
}
}
A program a bemenő n számot az első parancssor argumentumaként args[0] szöveges tömbelemben várja,
majd ebből az Integer osztály statikus parseInt() módszerével számot csinál, majd e számig felváltva 0 és 1
jegyeket ír ki. Ha a kedves Olvasó lefordítaná és futtatná a programot, akkor a következőt kapná:
[norbi@niobe ~]$ javac BinárisSorozat.java
[norbi@niobe ~]$ java BinárisSorozat 1
0
A program 185 betű, az inputja 1 betű, összesen 186 betű. Ha most a kedves Olvasó az n=10 értékkel futtatná,
akkor a következő kimenethez jutna:
[norbi@niobe ~]$ java BinárisSorozat 10
0101010101
A program 185 betű, az inputja 2 betű, összesen 187 betű. Az n=100 értékkel indítva:
[norbi@niobe ~]$ java BinárisSorozat 100
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101
A program 185 betű, az inputja 3 betű, összesen 188 betű. Ezerrel indítva:
[norbi@niobe ~]$ java BinárisSorozat 1000
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
01010101010101010101010101010101010101010101010101010101010101010101010101010101
0101010101010101010101010101010101010101010101010101010101010101010101010101010
10101010101010101010101010101010101010101
A program 185 betű, az inputja 4 betű, ez összesen 189 betű.
Összegezzük eredményeinket!
1.3. táblázat - A 01-et ismétlő bináris sorozat kezdőrészeinek vizsgálata.
Page 65
A programozásról
37 Created by XMLmind XSL-FO Converter.
HOSSZ BONYOLULTSÁG A KETTŐ HÁNYADOSA
1 186 186
10 187 18.7
100 188 1.88
1000 189 0.189
... ... ...
10000000 193 0.00000193
... ... ...
Jól láthatóan minél hosszabb a generált sorozat, annál inkább közel kerül a nullához a hányados, azaz a
hányados nullához tart, ha a sorozat n hossza a végtelenbe. Az iménti empirikus megalapozás után ebben
biztosak lehetünk, hiszen a bonyolultság, a program és inputja együttes hossza általánosan írva 185 + lg(n) + 1,
mert a 185 karakteres programot ebben a formában futtattuk:
[norbi@niobe ~]$ java BinárisSorozat n
ahol a parancssorban átadott n input szám leírásához lg(n) + 1 számjegy szükséges. A (185 + lg(n) + 1)/n pedig
zérushoz tart, az n tart végtelen esetén.
Ez azt jelenti, hogy a vizsgált sorozat hosszának növelésével a sorozat bonyolultsága nem tud lépést tartani.
Egy végtelen sorozatot akkor nevezünk véletlennek, ha a rá vonatkozó fenti hányados nem zérushoz, hanem az
egyhez tart. Tehát, ha a vizsgált sorozat hosszának növelésével egyforma mértékben növekedik a sorozat
bonyolultsága is!
Példánkból általánosíthatunk is: ha van olyan algoritmus, ami az n szám inputtal előállítja a sorozat első n
betűjét, akkor a sorozat nem lehet véletlen. Mert például H-val jelölve ennek az algoritmusnak a hosszát, a (H +
lg(n) +1)/n hányados ugyanúgy, mint fenti példánkban, a nullához fog tartani, ha n tart a végtelenhez.
1.22. példa - Véletlenszám generátorok
Az előző bekezdés általánosításának tükrében gondoljuk át, véletlen-e a következő program által generált
véletlen sorozat?
public class PszeudoVéletlen {
public static void main(String[] args) {
java.util.Random generátor = new java.util.Random();
for(;;)
System.out.print(generátor.nextInt(2));
}
}
Page 66
A programozásról
38 Created by XMLmind XSL-FO Converter.
Természetesen nem, mert az egy korábbi példában szereplő VéletlenSorozat osztályt úgy módosítva, hogy
nem 1000, hanem parancssorban kapott n számig nyomtat és feltéve, hogy a java.util.Random generátor
például éppen a 42-vel inicializálódott a jelen paraméter nélküli konstruktor hívás mögött, elő tudjuk állítani
éppen ennek a sorozatnak a kezdő részeit. S a fentiekhez hasonló számítással megkapjuk, hogy a vizsgálandó
hányados a nullához tart.
1.23. példa - Egy másik szabályos sorozat
Vizsgáljuk meg, hogy véletlen sorozat-e a A Chaitin-Kolmogorov bonyolultság című pont negyedik sorozata
mintájára képzett végtelen sorozat.
1.24. példa - Egy nem véletlen, de bonyolultabb sorozat
Próbáljunk meg egy olyan sorozatot „készíteni”, melyre a vizsgált hányados nem nullához tart! Ennek a célnak
megfelelően a következő FéligVéletlen osztályt úgy alakítottuk ki, hogy a sorozat felét, a második karakterét
is inputként kéri be. S a felhasználót arra kérjük, a program kérésére 0 vagy 1 jegyet írjon be, annak
megfelelően, hogy közben feldobott érméje fej vagy írás-e. Továbbá arra is figyelni kell, hogy a mindig
ugyanazt a kezdő részt adjuk be a program kérésére, azaz az Olvasónak a feldobott érme eredményeket meg kell
jegyeznie, s mindig csak az új pozícióra kell újra dobni.
public class FéligVéletlen {
public static void main(String[] args) throws Exception {
int db = Integer.parseInt(args[0]);
int [] sorozat = new int [db];
int fele = db/2;
java.io.BufferedReader inputCsatorna =
new java.io.BufferedReader(
new java.io.InputStreamReader(System.in));
for(int i=0; i<db; ++i)
if(i%2 == 1){
System.out.print("Az "+i+". tag: ");
String sor = inputCsatorna.readLine();
sorozat[i] = Integer.parseInt(sor);
} else
sorozat[i] = 0;
System.out.println();
for(int i=0; i<db; ++i)
System.out.print(sorozat[i]);
}
}
A program 469 betű hosszú.
Ha a kedves Olvasó lefordítaná és futtatva a programot elvégezne néhány próba futtatást, akkor az alábbiakat
kaphatná:
C:\...> javac FéligVéletlen.java
C:\...> java FéligVéletlen 1
0
Page 67
A programozásról
39 Created by XMLmind XSL-FO Converter.
C:\...> java FéligVéletlen 2
Az 1. tag: 1
01
C:\...> java FéligVéletlen 3
Az 1. tag: 1
010
C:\...> java FéligVéletlen 4
Az 1. tag: 1
Az 3. tag: 0
0100
C:\...> java FéligVéletlen 10
Az 1. tag: 1
Az 3. tag: 0
Az 5. tag: 1
Az 7. tag: 1
Az 9. tag: 0
0100010100
Mit mondhatunk a vizsgált sorozat véletlenségéről? Összegezzük eredményeinket!
1.4. táblázat - A félig véletlen bináris sorozat kezdőrészeinek vizsgálata.
HOSSZ BONYOLULTSÁG A KETTŐ HÁNYADOSA
1 469 469
10 469+10+2 48.1
100 469+50+3 5.22
1000 469+500+4 0.973
10000 469+5000+5 0.5474
... ... ...
10000000 469+5000000+8 0.5000477
... ... ...
láthatóan 0.5-höz tartunk és valóban a (469 + n/2 + lg(n) + 1)/n kifejezés, az n tart a végtelenhez határátmenet
esetén az 1/2-hez tart.
1.1.1.6. A megállási Omega valószínűség
„ÁDÁM
Úgyis nem ront-e majd el a halál?
A FÖLD SZELLEMÉNEK SZAVA
A vén hazugság e hiú szavát ne mondd,
ne mondd itt a szellemvilágban.
Page 68
A programozásról
40 Created by XMLmind XSL-FO Converter.
Egész természet átborzadna tőle.
Szentelt pecsét az, feltartá az Úr
Magának. A tudás almája sem
Törhette azt fel.”
—Madách Imre
Korábban már láttuk (programoztuk), hogy Turing gépeinket megadhatjuk csupán a 0 és az 1 jegyek leírásával.
Tegyük fel, hogy a világon összesen 10 Turing gép van és ezeket a következő biráris sorozatokkal „kódoltuk”.
Ezek a kódok mindamellett, hogy csak szimbolikusak - mivel ilyen kevés bittel természetesen nem menne a
lekódolásuk - nagyon speciálisak, mert figyeljük meg, hogy egyik kódszó sem kezdődik egy másik kódszóval.
Az ilyen kódolást nevezzük prefixnek. Ami a programozóknak egyébként természetes, mert a programok prefix
kódok, hiszen egyik program sem kezdődik egy másikkal:
Ha a kedves Olvasó megpróbálna egy ilyen felépítésű
public class Program {
}
}
programot lefordítani, akkor az alábbi fordítási hibát kapná:
Page 69
A programozásról
41 Created by XMLmind XSL-FO Converter.
C:\...> javac Program.java
Program.java:5: class, interface, or enum expected
}
^
Program.java:6: reached end of file while parsing
^
2 errors
Tegyük fel továbbá, hogy a 10 gép közül az alábbi 4 megáll, 6 gép viszont nem áll meg, azaz végtelen ciklusba
esik.
1.1.1.6.1. Chaitin gépek
A Turing gépek prefix kódolása azért fontos számunkra, mert a Kraft-Fánó-McMillan egyenlőtlenségből
[DEMETROVICS KÖNYV] tudjuk, hogy prefix kódokra teljesül. A hivatkozott egyenlőtlenség pontosan azt
fejezi ki, hogy a kódszó1, kódszó2, ... kódszón n kódszóból álló prefix kódra a
2|kódszó1
| + 2|kódszó2
| + ... + 2|kódszón
| <= 1 teljesül.
A mi esetünkben a kód a 01, 000, 001, 101, 100, 1100, 1101, 1110, 11110, 11111, az egyenlőtlenségbeli összeg
értéke pedig
2-2 +4*2-3 +3*2-4 +2*2-5 = 1
Tehát az egyenlőtlenség - mint ahogyan tudtuk is - valóban nem sérül példánkban sem. Mivel az összeg értéke
mindig a 0 és az 1 közé esik, ezért valószínűségnek tekinthetjük.
A Turing gépek megállási valószínűségének nevezzük azt a fenti típusú összeget, amelyben az összegzést a
megálló gépek kódjára végezzük el. Elképzelt, 10 gépes világunkban tehát a megállás valószínűsége
2*2-3 +2-4 +2-5 = 0,34375
Page 70
A programozásról
42 Created by XMLmind XSL-FO Converter.
A prefix Turing gépek (Chaitin gépek) világában ezt a megállási valószínűséget Omegának nevezzük.
([CHAITIN OMEGA], [JAVA PROG és OMEGA] és lásd még az irodalomjegyzék számos Omegával
kapcsolatos bejegyzését.)
1.25. példa - A Chaitin-féle Omega konstans ismeretében meg tudnánk oldani a
megállási problémát!
A [PARADOXON KÖNYV] könyvben bemutatott kapcsolódó gondolatmenet alapján ebben a példában
bemutatjuk, hogy Omega ismeretében meg tudnánk oldani a megállási problémát.
Vizsgáljunk egy tetszőlegesen megválasztott H bit hosszúságú programot! Az alábbi táblázat segítségével
megmutatjuk, hogy a Chaitin-féle megállási Omega konstans ismeretében meg tudnánk mondani, hogy
tetszőleges program megáll-e, azaz meg tudnánk oldani a megállási problémát, ami lehetetlen! Tehát nem
ismerhetjük meg a konstanst, az Omegának őriznie kell titkait az ember előtt...
A vízszintes tengelyen a programok szerepelnek hosszuk szerint, a függőlegesen a futási idő. Minden
időpillanatban elkezdjük futtatni az egy bittel hosszabb programokat. A kísérlet közben megálló programok 2 -
hosszuk értékkel növelik meg az Omega értékét, egészen pontosan OMEGAN jelölje az N. időpillanatig megálló
vizsgált, azaz maximum N hosszú gépek hosszával növelt összeget. Nyilván OMEGAN <= OMEGAN+1, ami
alapján menjünk addig előre az idővel, amikor az OMEGAN monoton közelítés már 1/2H értéknél közelebb van
az OMEGA határértékhez. Következzen ez be például a t=M időpillanatban. Tehát ekkor az OMEGA-
OMEGAM < 2-H. És most jön a trükk: ha a vizsgált H hosszú program eddig nem állt meg, akkor (most már, azaz
véges sok lépés után) tudjuk, hogy már nem is fog megállni, mert ha megállna, akkor 2-H értékkel növelné meg
az Omega approximációját, azaz ebben a megállás valamikori időpillanatában OMEGAvalamikori >= OMEGAM + 2-H
> OMEGA - 2-H + 2-H = OMEGA. Hoppá! Döbbenetes, nem igaz? Jó kis ellentmondás, miszerint az alulról
közelítő OMEGAvalamikori átlépte az OMEGA értéket!
Page 71
A programozásról
43 Created by XMLmind XSL-FO Converter.
Az Omega számnak számos további érdekes tulajdonsága is van, például ha a tizedes kifejtését egy
számjegyekből álló betűsorozatnak tekintjük, akkor ez a sorozat véletlen. Szemben például a Pi konstanssal, ami
ugyan nem gyöke egyetlen egyenletnek sem, de végtelen sorokkal tömören leírható, a Pi tizedes kifejtésének
bármely jegyei tetszőleges pontossággal meghatározhatók.
1.26. példa - A Pi közelítése
Az alábbi példa kapcsán javasoljuk elolvasni a [KAPCSOLAT REGÉNY] regényt, mely végén a Pi jegyeivel
színezett történet erős érzelmi motivációt adhat. A hivatkozott [KAPCSOLAT MOZI] filmet is bátran ajánljuk
az Olvasónak, de ebből a feldolgozásból sajnos a könyv kapcsán említett Pi konstanssal kapcsolatos részek
teljesen hiányoznak.
A Pi pontos értékével való ismerkedést kezdjük a Gregory-Leibniz formula bemutatásával.
public class Pi {
double közelítőÉrték;
public Pi(long n) {
double p = 0;
// Gregory-Leibniz formula:
// Pi/4 = 1 - 1/3 + 1/5 - 1/7 + ... + (1-(i%2)*2)/(2*i+1) + ...
for(long i=0; i<n; ++i)
p += (double)(1-(i%2)*2)/(2*i+1);
közelítőÉrték = 4*p;
}
public static void main(String[] args) {
long n = 1;
while(true) {
n *= 10;
Pi pi = new Pi(n);
System.out.println("pi ~ Pi(n) = " + pi.közelítőÉrték
+ ", n = " + n
+ ", Math.PI/Pi(n) = "
+ (Math.PI / pi.közelítőÉrték));
}
}
}
Ha az Olvasó fordítaná és futtatná a programot, akkor a következő kimenetét kapná:
[norbi@niobe ~]$ javac Pi.java
[norbi@niobe ~]$ java Pi
pi ~ Pi(n) = 3.0418396189294032, n = 10, Math.PI/Pi(n) = 1.0327936535639899
pi ~ Pi(n) = 3.1315929035585537, n = 100, Math.PI/Pi(n) = 1.0031931832582315
pi ~ Pi(n) = 3.140592653839794, n = 1000, Math.PI/Pi(n) = 1.0003184111600008
pi ~ Pi(n) = 3.1414926535900345, n = 10000, Math.PI/Pi(n) = 1.0000318320017856
pi ~ Pi(n) = 3.1415826535897198, n = 100000, Math.PI/Pi(n) = 1.0000031831090173
pi ~ Pi(n) = 3.1415916535897743, n = 1000000, Math.PI/Pi(n) = 1.0000003183099935
pi ~ Pi(n) = 3.1415925535897915, n = 10000000, Math.PI/Pi(n) = 1.00000003183099
pi ~ Pi(n) = 3.141592643589326, n = 100000000, Math.PI/Pi(n) = 1.0000000031832477
pi ~ Pi(n) = 3.1415926525880504, n = 1000000000, Math.PI/Pi(n) = 1.0000000003188645
pi ~ Pi(n) = 3.141592653488346, n = 10000000000, Math.PI/Pi(n) = 1.0000000000322917
pi ~ Pi(n) = 3.141592653578537, n = 100000000000, Math.PI/Pi(n) = 1.000000000003583
Page 72
A programozásról
44 Created by XMLmind XSL-FO Converter.
egészen addig, amíg egy Ctrl+c nyomásával meg nem állítja a végtelen ciklusban számoló programot.
(Ameddig mi a program futását engedtük, a Pi(n) = 3.141592653578537 10 tizedesig pontos eredményig
jutottunk.)
A képzeletbeli futtatás előtt próbálja elképzelni, megbecsülni az egyre növekvő n = n * 10 mellett a Pi(long
n) konstruktor időbonyolultságát, azaz futási idejét, majd vesse össze elképzeléseit a futtatás során szerzett
„izometrikus” tapasztalataival!
A már Javaban nem kezdő Olvasó első javítási ötlete bizonyára az, hogy a közelítéshez nem készítene külön Pi
objektumokat és azok Pi(long n) konstruktorában nem kezdené mindig elölről az összegzést, hanem inkább
az alábbi módon szervezné át az osztály kódját.
public class Pi {
double közelítőÉrték;
long meddigSzámoltuk;
public Pi() {
közelítőÉrték = 0;
meddigSzámoltuk = 0;
}
public double közelítés(long n) {
double p = 0;
// Gregory-Leibniz formula:
// Pi/4 = 1 - 1/3 + 1/5 - 1/7 + ... + (1-(i%2)*2)/(2*i+1) + ...
for(long i=meddigSzámoltuk; i<n; ++i)
p += (double)(1-(i%2)*2)/(2*i+1);
if(n > meddigSzámoltuk) {
közelítőÉrték = 4*(közelítőÉrték/4 + p);
meddigSzámoltuk = n;
}
return közelítőÉrték;
}
public static void main(String[] args) {
long n = 1;
Pi pi = new Pi();
while(true) {
n *= 10;
System.out.println("pi ~ Pi(n) = " + pi.közelítés(n)
+ ", n = " + n
+ ", Math.PI/Pi(n) = "
+ (Math.PI / pi.közelítés(n)));
}
}
}
A Pi jegyeinek nyomában
Ha az Olvasó futtatná a most vázolt programot, akkor tapasztalhatná, hogy az említett [KAPCSOLAT
REGÉNY] olvasásának befejezésekor keletkezett lelkesítő feszültségét ezekkel bizony nem tudta
levezetni-átvezetni a Pi jegyeinek önálló keresésében. Ha a kedves Olvasó valóban így érezne, akkor A
Pi jegyeinek nyomában matematikai témájú programozási mellékletünket és például a [PI KÖNYV]
könyvet ajánljuk figyelmébe. Itt és most csak az iménti Pi osztályunk két kiterjesztését mutatjuk be, a
RamanujanPi osztályban a hivatkozott [PI KÖNYV] alapján a Ramanujantól származó, még a
Page 73
A programozásról
45 Created by XMLmind XSL-FO Converter.
ChudnovskyPi osztályban, ugyancsak a hivatkozott [PI KÖNYV] alapján a Chudnovsky-féle
Ramanujan típusú formulát mutatjuk be.
1.27. példa - A Ramanujan és a Chudnovsky közelítő összegek formulái
public class RamanujanPi extends Pi {
double pTár = 0.0;
public double közelítés(long n) {
double p = 0.0;
double sz = (2.0 * Math.sqrt(2.0)) / 9801.0;
for(long k=meddigSzámoltuk; k<n; ++k)
p += (f(4*k)*(1103.0 + 26390.0*k))
/ (Math.pow(f(k), 4.0) * Math.pow(396.0, 4.0*k));
if(n > meddigSzámoltuk) {
pTár += p;
közelítőÉrték = 1.0/(sz*pTár);
meddigSzámoltuk = n;
}
return közelítőÉrték;
}
/** k!, azaz k faktoriális kistámítása */
public double f(long k) {
double f = 1.0;
for(long l=1; l<k; ++l)
f *= l;
return f;
}
public static void main(String[] args) {
RamanujanPi pi = new RamanujanPi();
for(int n=2; n<5; ++n) {
System.out.println("pi ~ Pi(n) = " + pi.közelítés(n)
+ ", n = " + n
+ ", Math.PI/Pi(n) = "
+ (Math.PI / pi.közelítés(n)));
}
}
}
Ha az Olvasó fordítaná és futtatná a programot, akkor a következő kimenetét láthatná:
[norbi@niobe ~]$ javac RamanujanPi.java
[norbi@niobe ~]$ java RamanujanPi
pi ~ Pi(n) = 3.141592710907427, n = 2, Math.PI/Pi(n) = 0.9999999817552309
pi ~ Pi(n) = 3.1415927109074255, n = 3, Math.PI/Pi(n) = 0.9999999817552313
pi ~ Pi(n) = 3.1415927109074255, n = 4, Math.PI/Pi(n) = 0.9999999817552313
Érdekességként megjegyezhetjük, hogy ugyanaz az R. W. Gosper, akinek „sikló ágyú” nevű sejtautomata
élőlényét bemutatjuk a Az R. W. Gosper-féle sikló ágyú élőlény bemutatása [306] című táblázatban, 1985-ben
erre a formulára alapozva, 17.526.200 tizedesjegyig határozta meg a Pi értékét.
Page 74
A programozásról
46 Created by XMLmind XSL-FO Converter.
Mivel mi csupán a 64 bites lebegőpontos aritmetikát használjuk, csak a fent idézett számítást tudjuk futtatni,
azaz a Pi(n) = 3.141592710907427 6 tizedesig pontos eredményig jutottunk.
public class ChudnovskyPi extends RamanujanPi {
public double közelítés(long n) {
double p = 0.0;
for(long k=meddigSzámoltuk; k<n; ++k)
p += ((1-(k%2)*2)*f(6*k)*(13591409 + 545140134*k))
/(f(3*k)*Math.pow(f(k), 3.0)*Math.pow(640320, 3*k+3.0/2.0));
if(n > meddigSzámoltuk) {
pTár += p;
közelítőÉrték = 1.0/(12.0*pTár);
meddigSzámoltuk = n;
}
return közelítőÉrték;
}
public static void main(String[] args) {
ChudnovskyPi pi = new ChudnovskyPi();
for(int n=2; n<5; ++n) {
System.out.println("pi ~ Pi(n) = " + pi.közelítés(n)
+ ", n = " + n
+ ", Math.PI/Pi(n) = "
+ (Math.PI / pi.közelítés(n)));
}
}
}
Ha az Olvasó fordítaná és futtatná a programot, akkor a következő kimenetét láthatná:
[norbi@niobe ~]$ javac ChudnovskyPi.java
[norbi@niobe ~]$ java ChudnovskyPi
pi ~ Pi(n) = 3.141592653589764, n = 2, Math.PI/Pi(n) = 1.0000000000000093
pi ~ Pi(n) = 3.141592653589764, n = 3, Math.PI/Pi(n) = 1.0000000000000093
pi ~ Pi(n) = 3.141592653589764, n = 4, Math.PI/Pi(n) = 1.0000000000000093
(Mivel mi csupán a 64 bites lebegőpontos aritmetikát használjuk, csak a fent idézett számítást tudjuk futtatni,
azaz a Pi(n) = 3.141592653589764 13 tizedesig pontos eredményig jutottunk.)
A Pi jegyeinek nyomában matematikai témájú programozási mellékletünk Pi jegyeit boncoló részében már olyan
osztályokat is tárgyalunk, amelyekkel a 64 bites lebegőpontos aritmetikát használva is remek programozási
élményeket gyűjthetünk. Most csupán ízelítőül vágjuk be ide a PiBBP osztályunkkal kiszámolt 1000 darab
számjegyét a Pi hexadecimális kifejtésének, a kifejtés 1000001. jegyétől.
[norbi@niobe ~]$ javac PiBBP.java
[norbi@niobe ~]$ java PiBBP
6C65E52CB459350050E4BB178F4C67A0FCF7BF27206290FBE70F93B828CD939C475C728F2FDB0CB9
23CF52C40D631D4DB2E98340AA25A6F07DB685C0A9C04F3F6E667CFD6E1764C83ECA94E79661FC18
0E6AEF581987E79E13278712CB01255E8CE4D9E048F782D756370548FB0778323CF2074C2716D121
639F1DD5A31EF6C242676B3783AD528852CCA52A9B4F999C526B0750859AEEC9CE6635B30996A210
CD419D5FD47A4E7AAF906E26A4CCF99A2E493BBB5E7D5E0B94F15196DA8CD1A0C57FE03A629B2D58
42317C173D163EA8717B46930EE0FE82FEC4B01016F155FB446AA6958EAD9265EC0C914CB84755DD
1BCE5100C23804D67A787BEC57CD7D8E190B3F55E3D2558927215504F141AC8B0BA836F7781E1966
Page 75
A programozásról
47 Created by XMLmind XSL-FO Converter.
4EFA8B22BEB3816A70F7210E4784A1F37762361286448CD051BCE3A4CE156D70CDBA256C1A36C386
48633C8F13A53405795635084A2DEAF3B9066BC3863BB07447DDDBDE5644034A6893E3E1CFDB3696
31BAA4240D93F17F667F7C51ABF076F7C1BB35DECC240153F4817A579CBD1DAC895E8555929D1ADA
3C787A0BF2881BBC44C4BE505E91FE5A28B9BA47D4845B7639239AD71D8B63BF9D23B2CC88C9D39C
033B0482F5F801D778BBB734EA8B1BE878D129514BFA5C4A6D60E80CF4B14A2A5673992B18397230
54BD44F767B03245F2873973EF6D84B2B96EFC9A
1.1.1.7. Eldönthetetlenség a formális axiomatikus elméletekben
„ANGYALOK KARA
Szabadon bűn és erény közt
Választani, mily nagy eszme,”
—Madách Imre
A tudás fájának gyümölcséből fogyasztva megadatott, hogy dönthessünk igazság és hamisság között, de a
döntés nem egyszerű. Az axiomatikus módszer ezt segíti. Néhány elfogadott igazságból, mechanikus levezetési
szabályok segítségével további igazságokat gyártunk. Saját mindennapi döntéseinkben többször, másokéban
meg még jóval többször vélünk észre venni részrehajlást. Ebben az értelemben az axiomatikus módszer egy
instant eljárás: „csak felöntjük vízzel”, azaz levezetünk; miközben a döntés objektív marad, nem függ az
érvelőtől, a levezetést végzőtől, azaz a „bárki ezt hozta volna ki, így én magam is erre az eredményre jutottam
volna” megközelítés megadja az objektivitás élményét. Ehhez a levezetéshez persze az emberi beszéd
túlságosan is árnyalatgazdag - ezt elfogadni elég a választási kampányok sokszor végtelennek tűnő meddő
dialógus-párbeszédeire gondolni, ahol megszokott, hogy az A állítást és annak tagadását is minden különösebb
nehézség nélkül bizonyítják a jelöltek - ezért a levezetéseket egy speciális nyelven, egy matematikai logikai
nyelven kell elvégeznünk.
A matematikai logikai nyelvek nagyon közel állnak a programozók gondolkodásához, akik ezért könnyen tudják
beszélni, pontosabban írni és olvasni is. Tekintsünk meg egy konkrét arról szóló példát, hogyan beszélünk egy
ilyen matematikai logikai nyelven, ami legyen a [DRAGÁLIN KÖNYV] könyvben ismertetett Ar nyelv.
1.28. példa - Az Ar klasszikus elsőrendű matematikai logikai nyelv
A hivatkozott [DRAGÁLIN KÖNYV] Ar nyelv a számok világabeli pontos állítások leírását teszi lehetővé,
segítségével a számokra vonatkozó állításokat lehet formalizálni.
I. Az Ar típusos nyelv, egyetlen típusa a szám típus, a szám típusú változók nevei betűkből álló azonosítók, de
tipikusan az egyetlen betűből álló változóneveket szoktuk használni: a, b, c, ..., x, y, z, ...
II. Az Ar nyelvben egyetlen konstans, a 0 szimbólummal jelölt konstans van.
III. Az Ar nyelvben a szavak felépítésére három függvény szolgál, ezeket a S(), +(,), *(,) szimbólumok
jelölik. A változónevek és a konstans egyszerű szavak, a függvények segítségével ezekből összetett szavak
építhetők az alábbiak szerint
i. az Sszó szó egy összetett szó (S a szó értékének eggyel való növelése)
ii. a szó+másikszó szó egy összetett szó
iii. a szó*másikszó egy összetett szó Például az SS0, S(0+S0), S(0+S0)*SS0 összetett szavak.
IV. Az Ar nyelv állításai vagy mondatai (szó = másikszó) alakúak. A mondatokból összetett mondatok
alkothatók az ÉS, a VAGY, a NEM és a KÖVETKEZIK logikai jelekkel, az egzisztenciális (LÉTEZIK) és az
univerzális (MINDEN) kvantorok segítségével. Például a LÉTEZIKc (a+c=b) ÉS (!(a=b)) Ar nyelvű állítás
felolvasva: van olyan c, amit az a változóhoz adva b-t ad, azaz a < c.
Az Ar nyelven pontosan meg tudjuk fogalmazni a számok világabeli állításainkat. Írjuk fel például egy Ar
nyelvű mondat formájában, az (a < b) := (LÉTEZIKc (a+c=b) ÉS (!(a=b))) , hogy
Page 76
A programozásról
48 Created by XMLmind XSL-FO Converter.
• minden számnál van nagyobb: MINDENx (LÉTEZIKy(x < y))
• van olyan szám, aminél nincs nagyobb: LÉTEZIKy (MINDENx (x < y))
• a számok végtelen sokan vannak: MINDENx (LÉTEZIKy(x < y))
• véges sok szám van: LÉTEZIKy (MINDENx (x < y))
1.29. példa - Saját terület formalizálása
Programozóként a formalizálással kapcsolatban az automatikus tételbizonyítás egy izgalmas terület, ahol olyan
programokat tudunk írni, amelyek képesek formulák igazságának vagy hamisságának mechanisztikus
eldöntésére. Ehhez első lépés a vizsgált terület formalizálása, melynek során ki kell alakítanunk egy nyelvet,
amin tárgyalhatjuk a vizsgált területet. Most példaként foglalkozzunk a programokkal! Egyetlen típusunk legyen
a program típus, a program típusú változók nevei betűkből álló azonosítók, de tipikusan használjuk az x, y, z, ...
betűket. Jelölje E(x), hogy az x program esztétikus, J(x), hogy jó.
• Az esztétikus programok jók: MINDENx (E(x) KÖVETKEZIK J(x))
• Csak az esztétikus programok jók: MINDENx (J(x) KÖVETKEZIK E(x))
• Az esztétikus és csak az esztétikus programok jók: MINDENx ((E(x) KÖVETKEZIK J(x)) ÉS (J(x)
KÖVETKEZIK E(x)))
• Minden program, kivéve a nem esztétikusakat, jó: MINDENx (NEM E(x) KÖVETKEZIK J(x))
• Esztétikus program is lehet rossz: LÉTEZIKx (E(x) ÉS NEM J(x))
• Nem minden esztétikus program jó: NEM MINDENx (E(x) KÖVETKEZIK J(x))
Egy adott tudományterület axiomatikus formális elmélete egy logikai nyelvből és az axiómákból áll. A
természetes számok vizsgálatára létrehozott elméletet az Ar nyelv és a Peano-féle axiómarendszer alkotja. Egy
elméletet teljesnek nevezünk, ha a hozzá tartozó nyelv bármely mondatáról eldönthető, hogy ő vagy éppen az
ellenkezője vezethető le az elméletben.
De az axiomatikus módszernek is megvannak a maga korlátai, melyeket Hilbert nagy szomorúságára Gödel
híres inkompletibilitási tételei formájában tárt fel 1931-ben. A sors szeszélye, hogy éppen egy nappal azelőtt
tette közzé eredményeit, mielőtt Hilbert azóta híressé vált mondásában hitet tett a tudásról alkotott alábbi
meggyőződése mellett: „Tudnunk kell, tudni fogjuk.” Ezzel a hittel szemben az első ilyen inkompletibilitási
tétel állítása, hogy a formális axiomatikus elméletek nem teljesek. Példánknál maradva vannak olyan állítások,
melyek igazak a természetes számok körében, de az Ar elméletben sem bizonyítani, sem cáfolni nem lehet őket,
azaz sem a megfelelő Ar nyelvi mondat, sem annak tagadása nem vezethető le az elméletben. A témakör pontos
fogalmait az Olvasó a [DRAGÁLIN KÖNYV] könyvben találja meg.
A Chaitin-Kolmogorov bonyolultsággal kapcsolatban megismert fogalmainkat felhasználva könnyen tehetünk
igaz, de bizonyíthatatlan állításokat. Lássuk a [VITÁNYI KÖNYV] ilyen bevezető példájának egy programozói
megfogalmazását. Ez a hivatkozott [VITÁNYI KÖNYV] példa arról szól, hogy léteznek bonyolultságot
használó igaz, de bizonyíthatatlan - azaz Gödeli - állítások. Nevezetesen az állítás az, hogy az „x sorozat
véletlen” állítás nem bizonyítható. Ennek bizonyításaként tegyük fel, hogy mégis az! Ekkor tudunk egy olyan
programot írni, amely generálja a megfelelő elméletbeli levezetéseket és előbb-utóbb rátalál az indirekt
feltevésünk szerint létező levezetésre, azaz a bizonyításra...
A megfelelő formális axiomatikus elméletet F-el jelölve és elképzelve, hogy az F elméletet 0, 1 jegyekkel
lekódoltuk, felülről becsülhetjük a bonyolultságát, azaz K(F)<=|F|<=f az x sorozat véletlen állítás pedig
szemantikailag azt jelenti, hogy K(x)>=|x|
Page 77
A programozásról
49 Created by XMLmind XSL-FO Converter.
Amikor a program kiírja az x-et, az if utasítás fejéből láthatjuk, hogy |x|>f, ezt felhasználva az indulásnál tett
felső becslésünknél K(F)<=|F|<=f<|x| adódik, azaz röviden K(F)<|x|. Ha a két bonyolultságos egyenlőtlenségre
pillantunk, szépen látszik az ellentmondás:
• K(F)<|x|
• K(F)>=|x|
Megjegyezhetjük, hogy az automatikus tételbizonyítással kapcsolatos logikai kutatások szülték az olyan, a
deklaratív programozási paradigmák alá sorolt programozási nyelveket, mint például a Prolog. A A
programozás egy filogenetikai törzsfája című pont alatt részletesebben olvashat a programozási paradigmákról.
1.1.2. A programozás evolúciója
„A BASIC után PASCAL következzék (felfelé) vagy ASSEMBLER (lefelé)? A
matematikusoknál a PASCAL-nak, a diákoknál az ASSEMBLER-nek van nagyobb tábora.” —Marx György
Ha a kedves Olvasó egy pillantást vet a [LEVÉNEZ IDŐVONALAK] programozási nyelveket bemutató
munkájára, akkor az a benyomása támadhat, hogy témánk, a programozás egy élő, önmagát szervező
diszciplína. A programozási nyelvek arra szolgálnak, hogy kommunikálhassunk új szervünkkel - egyik legújabb
játékszerünkkel - a gyors számításokat végző gépekkel. Ez sok ember számára nagyon rejtélyes, majd idővel
nagyon izgalmas tevékenység. De ennek a rejtélyből az izgalomig hatoló megismerési folyamatnak a
mellékterméke a fejlődés, s ami az egyik embernek valamikor izgalmas volt, úgy lesz már inkább csak
(történeti) érdekesség egy későbbi valakinek, de a sors igazságossága, hogy ez a lánc végtelenül folytatódik:
minden későbbi valaki valamikorivá válik. Amit közben felhalmoznak, az az informatikai kultúra.
És ki tudja milyen lesz a jövője ennek a kultúrának? Például a gépek átalakíthatják annyira a mindennapjainkat,
hogy egy program forrásszövegének elemzéséről szóló óra megjelenhet egy vers elemzéséről szóló középiskolai
óra mellett esélyes vetélytársként?
Kevésbé irodalmi, de költői kérdést felvetve: egyszerűbb ma a programozás, mint tegnap? Azonnal vágnánk rá,
hogy igen, pedig lehet, hogy inkább mégsem! Mi nem akarjuk eldönteni, csak egy további kérdéssel sugallni,
hogy szerintünk mi a válasz. Egy C vagy egy Java programozás tankönyv Chaitin-Kolmogorov bonyolultsága
nagyobb-e?
Meggyőződésünk, hogy amíg a számítógépeink nem az ember központi idegrendszerének emberi tudatot
létrehozó működése mintájára működnek, addig a programozást mindig tanulnunk kell. Márpedig máshogy
működnek és a jövő (például a lehetséges kvantum programozás?), vagy az azt követő jövő még jobb gépei,
méginkább máshogy működhetnek majd. Ismét csak egy sugalmazó kérdést feltéve: mit érezhetne egy 10 éve
napi gyakorlatban dolgozó imperatív programozó, amikor tegnapra Prolog fejlesztővé kellene átképeznie
magát?
1.1.3. A programozás egy filogenetikai törzsfája
„A matematika Wigner Jenőt lenyűgöző csodálatos hatékonysága a szem látáshoz való
alkalmazkodásához hasonlóan a szelektív evolúcióval magyarázható. Ha napjaink
matematikája hatékony, az csak azért van, mert a tegnap nem eléggé hatékony matematikáját
könyörtelenül kiirtották és kicserélték.”
Page 78
A programozásról
50 Created by XMLmind XSL-FO Converter.
—Stanislas Dehaene
1.1.3.1. Megjegyzések a törzsfához
A Turing kiszámítható programozás alatt azt értjük, hogy minden olyan dolgot, amit a belőle leszármazó
programozások alól származó programozási nyelveken meg lehet csinálni, azt Turing géppel is meg lehet
csinálni.
A DES törés a [DES TÖRÉS] cikkben ismertetett konkrét DNS számítás. A DNS számítások tipikusan a „brute
force” jellegű feladatmegoldásra alkalmasak, ahol az összes potenciálisan lehetséges megoldás megvizsgálása
során kapjuk a tényleges megoldásokat. A DNS számítások apropója, hogy a lehetséges összes megoldást
egyszerre, párhuzamosan próbálják ki. Az említett DES eljárással akkor találkozhat az Olvasó, amikor egy
UNIX vagy Linux rendszerű gépre bejelentkezik, ekkor a begépelt jelszaván, például a matyi2006
karaktersorozaton a bejelentkeztető program végrehajtja a DES algoritmusát, aminek eredménye, a példánál
maradva a $1$wZ3bMDHK$Xogj2CHjy4.o3MEB2nhp00 karaktersorozat. Jelszavunk ilyen kódolt képét tárolja a
gép, Linux alatt például a /etc/shadow állományban. A két minta összevetésétől függ, hogy a bejelentkeztető
program beenged-e minket a rendszerbe vagy sem. A DES törése annyit tesz, hogy meg tudjuk mondani a
kódolt képhez tartozó eredeti szót. Csak érdekességként jegyezzük meg és ez a megjegyzés nem kapcsolódik a
DNS számításokhoz, hogy a jelszavakat törő programok (ilyen például a cracklib) célja, hogy a rendszergazda
rávegye a felhasználókat, hogy - a teljes rendszer védelmében - megfelelően bonyolult jelszavakat válasszanak
Page 79
A programozásról
51 Created by XMLmind XSL-FO Converter.
maguknak. A [PROGRAMOZÓ PÁTERNOSZTER JEGYZET] jegyzetben arra láthatunk példát, hogyan
törhetjük, az említett céllal, a felhasználóink jelszavait egy másik, a John the Ripper programmal.
Az említett LEGO Mindstorms NXT korábbi változatát, a LEGO© Mindstorms® Robotics Invention System
robotot egyébként Javaban is lehetett programozni a [LEJOS JAVA] rendszer segítségével. A [JÁVÁCSKA
PORTÁL] és a [JÁVÁCSKA BARÁTAI] lapjain számos olyan előadás prezentációnk található, ahol ezt
mutatjuk be, ilyen például a [TANÁRKÉPZÉS EA.] bemutató.
1.1.3.2. Röviden az egyes programnyelvekről
1.1.3.2.1. Ada
Az Amerikai Hadügyminisztérium támogatásával tervezett eljárásorientált nyelv, a NATO egyik hivatalos
programozási nyelve. Bonyolult, az eljárásorientált paradigma minden eszközét tartalmazó nyelv. Az Ada 95
verzió óta objektumorientált konstrukciókat is tartalmaz.
1.1.3.2.2. C
Az egyik legsikeresebb eljárásorientált nyelv. Gépi kódú logikával és azt közelítő sebességgel rendelkező
programok írhatók benne. Speciális a mutató típusa. Nincs dinamikus szemantika ellenőrzés. A memóriát a
programozó kezeli. Rendszerprogramozásra is használják. Szabad felhasználású implementációi is léteznek (pl.
a gcc).
1.1.3.2.3. C++
Az egyik legsikeresebb objektumorientált nyelv, a C kiterjesztése. Többszörös öröklődést, dinamikus és statikus
kötést, általánosított referencia típust, speciális típuskényszerítést tartalmaz. A memóriát a programozó kezeli,
nincs automatikus szemétgyűjtögetés. Szabad felhasználású implementációi is léteznek (pl. a gcc).
1.1.3.2.4. C#
Az OO paradigmából Java-elveket megvalósító, a .NET keretrendszerbe integrált nyelv. Fejlett vizuális
eszközkészlettel és speciális hatásköri eszközkészlettel (névterek) rendelkezik.
1.1.3.2.5. Delphi
A Pascal objektumorientált változata, amelyben az adatbázis-programozás is lehetséges.
1.1.3.2.6. Java
A Java egy „majdnem” tiszta OO nyelv. A C++ egy továbbfejlesztett változataként jött létre a nyílt elosztott
rendszerek programozási nyelveként. Tartalmaz eljárásorientált elemeket, de programozni benne csak az OO
paradigma mentén lehet. Tervezésénél alapvető volt a biztonságos kód írásának követelménye. A Java egyszeres
öröklődést, késői kötést, automatikus memóriakezelést valósít meg. Vannak benne primitív típusok, ezek
megfelelnek az eljárásorientált egyszerű típusoknak. Van változója, ami primitív típusú értéket, vagy
objektumreferenciát vehet fel értékül. A referencia típusú változók mindig a hivatkozott objektumot adják.
Kifejezésfogalma teljesen C-szerű. A módszereket függvények segítségével, az attribútumokat változókkal lehet
megadni. Ismeri a csomagot, ez a fordítási egysége. Bezárási eszközrendszere négy szintű. A publikus, privát és
védett szintet explicit módon kell megadni. Alapértelmezett a csomag szintű láthatóság. A Javának hatékony
kivételkezelő és párhuzamos programozást lehetővé tevő eszközrendszere van, az 5-ös verzióba már beépítették
a generikust is. Az interfész egy speciális viselkedésmód absztrakció implementálását lehetővé tevő eszköz. A
Java fordítóprogram egy közbenső formára, az ún. bájtkódra fordít, amelyet aztán a Java Virtual Machine
(JVM) interpretál. Ez segíti a hordozhatóságot.
1.1.3.2.7. JavaScript
A web programozás kliens- és szerveroldali szkript nyelve. Objektumalapú és eseményvezérelt. HTML
dokumentumok kezelését teszi lehetővé.
1.1.3.2.8. Lisp
Page 80
A programozásról
52 Created by XMLmind XSL-FO Converter.
A funkcionális paradigmát bevezető nyelv. Nagyon sok verziója létezik, közülük a dinamikus objektum
fogalommal rendelkező CLOS a leghíresebb. Az eredeti Lisp sok imperatív jellegű eszközt tartalmazott, nem
volt tisztán funkcionális.
1.1.3.2.9. Perl
Az egyik legsikeresebb szkript nyelv. Objektumorientált. Reguláris kifejezései igen hatékony és tömör
programozást tesznek lehetővé. A webprogramozásban jelentősége igen nagy. A szerveroldali CGI programozás
alapeszköze.
1.1.3.2.10. PHP
A szerveroldali web programozás szkript nyelve. Nyílt forráskódú. Legfontosabb felhasználási területe az
adatbázis-elérés. Közel két tucat adatbázis-kezelőhöz rendelkezik vezérlőtámogatással.
1.1.3.2.11. Prolog
A logikai paradigma első és legelterjedtebb nyelve. Sok verziója létezik, köztük objektumorientált is. Az
elsőrendű predikátumkalkuluson alapul. A Prolog nyelvi rendszer középpontjában egy következtető gép áll. A
nyelvben vannak tiszta logikai, metalogikai és logikán túli eszközök is.
1.1.4. Bepillantás napjaink gyakorlatába
A [JAVA.SUN.COM] lapjain számos helyen találhatunk a Java technológia elterjedtségét bemutató adatokat.
Emeljünk ki néhányat, például a [JAVA MINDENÜTT] című lapról! Mit saccolunk, mennyi Java programozó
lehet világszerte? A hivatkozott lap szerint a Java nyelven fejlesztők családja, a Java fejlesztői közösség immár
több, mint 5 millió programozót tömörít. Ezzel a Java fejlesztői közösség a legnagyobb programozói közösség
az ember által ismert világegyetemben. Mit programoznak a közösség tagjai? Ugyancsak a lap adatainak
tanúságai szerint majdnem 2 milliárd Java kártyát, 800 millió Javával eladott személyi számítógépet és 1.2
milliárd Javas mobiltelefont! Érdemes azon elgondolkodni, hogy az Olvasó háztartásában mennyi Java Virtuális
Gép van? Van például Javas mobilja?
1.1.5. Néhány látomás: a jövő programozása
„Elképzelhető, hogy az emberi értelemnek ugyanúgy vannak határai, mint az állati
intellektusnak. Egy kutya sok mindent megtanulhat, de a szorzótáblát lehetetlen neki
megtanítani.” —Wigner Jenő
Kognitív értelemben sokszor közelebb érezzük magunkhoz a számítógépeket, mint az állatokat, talán éppen
azért, mert a gépek bármi nehézség nélkül kinyomtatják a szorzótáblát. Ezt az érzést több megfigyelésünk erősíti
meg nap mint nap: például amikor LEGO robotunk, végre már, éppen kitalál a padlón könyvhalmokból
rögtönzött labirintusból, vagy a híradó bemutatja az éppen legcsillogóbb humanoid robot kacsintását. Mégis
könnyen elképzelhetőnek tartjuk, hogy ebben az érzésben csalatkozni fogunk. Nemcsak azért, mert mi -
szemben a számítógépekkel, akik viszont nem - ugyancsak megküzdöttünk a szorzótáblával.
Érdekes, hogy a mesterséges intelligenciát, a mesterséges intelligencia kutatásában való hitelességet senki nem
vitatja el az informatikusoktól. De ez egyben annak is alátámasztása, hogy a mesterséges intelligencia tárgya
mára túlontúl eltávolodott a természetes intelligencia reprodukálásától, az „Asimovi gépek” [ASIMOV
REGÉNY], [ASIMOV MOZI] létrehozásától.
Ezért olvashatunk újongva az irodalomban az Orch OR, azaz Orchestrated Objective Reduction, [ORCH OR]
modellről, mert a modellezett jelenség a tudat. Egyébként a modell egyben az interdiszciplinaritás példamutató
zászlóshajója is, lévén egyik kidolgozója Roger Penrose fizikus, a másik Stuart Hameroff, aki aneszteziológus.
Fontos, hogy elméletük cáfolható, maguk adnak meg olyan a modellhez kapcsolódó tulajdonságokat, melyek
sérülése a modell módosítását vagy természetesen akár elvetését is implikálhatja.
Page 81
A programozásról
53 Created by XMLmind XSL-FO Converter.
A kvantum programozást azért származtattuk a Turing kiszámítható programozásnál bővebb nem Turing
kiszámítható programozás dobozból, mert a kvantum gépek képesek valódi véletlenszámok előállítására
[VÉLETLEN MŰSZER].
A Penrose-Hameroff Orch OR modell interdiszciplináris jellegét az adja, hogy a modell egyaránt operál
biológiai, számítástudományi és fizikai alapokon. Biológiai vonatkozás, hogy a modellbeli számításokat a
sejtváz mikrotubulusai végzik, mert a számítások alapvető kapcsolóeleme a mikrotubulus csövecskéket felépítő
tubulin fehérjék térbeli szerkezete. Számítástudományi vonatkozás, hogy ezek a globuláris fehérjék a modell
szerint egy hexagonális rácsba szervezett sejtautomata hálózatként működnek. S végül, de elsősorban fizikai
vonatkozás, hogy ennek a sejtautomatának van olyan periodikusan jelentkező működési fázisa, mely
közvetlenül kapcsolódik a természet legmélyebb ismert elméletéhez, a kvantummechanikához. Mert ebben a
fázisban a sejtautomata cella következő állapotát nem közvetlenül és lokálisan a hat hexagonális cella állapota,
hanem holisztikusan, a Penrose által bevezetett, a gravitációhoz feltételezetten kapcsolódó objektiv
állapotvektor redukció határozza meg.
1.1.5.1. A Penrose-Hameroff Orch OR tudat modell
„Ha most lennék fiatal kutató, akkor a szellem-test problémát tanulmányoznám. Ez a 21.
század nagy kihívása.” —Ilya Prigogine
Napjainkban igen népszerű az agy-számítógép, s ennek megfelelően a idegrendszer-szoftver analógia, aminek
gyökerei talán Neumann az agyat és a számítógépet összehasonlító [GÉP és AGY] könyvében gyökereznek.
Neumann itt említi, hogy az idegrendszer egy „rövid program”, avagy mai terminológiával egy interpreter
lehet.
Az interpreterek tipikusan egy magasabb absztrakciós szintet vezetnek be az őket futtató réteg felett.
Gondoljunk csak a Java Virtuális Gépre mint interpreterre! A Java platform című kép mutatja, hogy a Java
Virtuális Gép elrejti az őt futtató hardverek különbözőségeit. Ebben az értelemben a különböző hardvereket
most a fonalférgek, a patkányok, a kutyák végül a majmok agyán át az ember agyáig képzelhetnénk.
Az Orch OR modell is egy számításos modell, abban tér el az uralkodó neuron hálózatos számítási modelltől,
hogy alapvető szerepet kap benne két olyan jelenség, amit az ember a legelemibbnek ismer a természetben, ez a
kvantummechanika és a gravitáció. Kicsit sántító analógiával olyan ez, mintha a fent említett - s persze most
csak ezen analógia megfogalmazása miatt feltételezett - interpreterünkön futó programunkat gépi kódú
részekkel kéne kiegészítenünk, azaz a használt absztrakciós szintünknél - ami ebben az esetben a Java
programozás - mélyebb szinteken is kellene dolgoznunk, pontosabban a legmélyebb szinten, analógiánkban gépi
nyelven. Egyébként vannak, akik éppen ezen analógia miatt fejezik ki kételkedésüket az Orch OR modell
tekintetében. Persze e kritikájukat nem a jelen interpreteres analógiánk alakjában fogalmazzák meg a tézisük
védelmében, hogy a tudat működését a klasszikus fizika keretein belül kell megmagyarázni, vagy még inkább a
triviálisan a fizikára, kémiára, biokémiára épülő, de immár a saját maga szabályai alapján működő biológiára,
idegtudományra: „A tudat sem a kémia alagsorában, sem a fizika pincéjében nem lakik” [GONDOLKODÓ
KÖNYV].
Page 82
A programozásról
54 Created by XMLmind XSL-FO Converter.
Az Orch OR modell általános vitája köré épül a [PENROSE-HAWKING KÖNYV] könyv, melyből kritikus és
támogató hangokat is kiemelhetnénk, s mindenképpen ajánljuk az ebben az irányban érdeklődő kedves Olvasó
figyelmébe. A modell természetesen az előző bekezdés végének idézetétől eltérő indíttatású kritikákat is kap. A
leginkább idézett ilyen [TEGMARK DEKOHERENCIA CIKK].
Való igaz, hogy az Orch OR modell egy erősen spekulatív jellegű modell, de mint egy potenciálisan lehetséges
új paradigma minden olyan érdeklődőnek és főleg programozónak felkeltheti az érdeklődését, aki többre tartja a
tudatot egy Turing gépnél.
1.1.5.1.1. Hameroff mikrotubulus sejtautomatái
„Az ember ezt, ha egykor ellesi,
Vegykonyhájában szintén megteszi.”
—Madách Imre Az ember tragédiája
Hameroff már korábbi munkáiban felveti annak lehetőségét, hogy az agyi számítások alapvető kapcsolóeleme
nem az idegsejt, hanem a minden eukarióta sejtben megtalálható mikrotubulus [HAMEROFF
MIKROTUBULUS]. Penrose [CSÁSZÁR KÖNYV] ismeretterjesztő könyvének olvasása során [HAMEROFF
INTERJÚ] jött Hameroff ötlete, hogy az agyi kvantum számítások a mikrotubulusokban mehetnek végbe.
Az eukarióta sejtek citoszkeletonját, sejtvázát háromféle fehérjefonalakból felépülő hálózatok alkotják, egyikük
a mikrotubuláris hálózat. A citoszkeleton adja meg a sejt alakját, biztosítja a sejten belüli anyagszállítást, esetleg
adott esetben még a sejt mozgását is, ha például a csillókra gondolunk. A mikrotubulusok kétféle tubulin
fehérjéből felépülő csövecskék. A kétféle tubulin egymással tubulin dimernek nevezett egységet alkot, melyek
egymás után fűzve a protofilamentumokat alkotják. Tipikusan 13 ilyen protofilamentum szál egy hengerpaláston
párhuzamosan elhelyezkedve alkotja a mikrotubulust [SEJTBIO 1], [SEJTBIO 2]. A tubulin dimerek
elhelyezkedését hexagonális ráccsal modellezhetjük, ahol egy tubulin dimernek hat tubulin dimer szomszédja
van [MIKROTUBULUS 1].
Page 83
A programozásról
55 Created by XMLmind XSL-FO Converter.
A tubulin dimerek két egymáshoz nagyon hasonló, a fehérjemolekulák konformációja, térszerkezete
meghatározta állapotban léteznek. Ezért kétállapotú kvantummechanikai rendszereknek tekinthetők. Ahogy már
említettük, Hameroffnak éppen Penrose [CSÁSZÁR KÖNYV] ismeretterjesztő könyvének tanulmányozása
során jutott eszébe, hogy a mikrotubulusok ideális otthont biztosíthatnak a Penrose által javasolt objektív
redukciónak nevezett feltételezett folyamatnak.
1.1.5.1.2. Penrose objektív redukciója
„Így bármilyen furcsán hangzik, logikailag lehetséges, hogy magában az objektív világban a
fizikai mennyiségeknek soha nincs egy-egy jól definiált értékük; csak egy-egy eloszlásuk van,
amelyben a lehetséges értékek egyidejűleg léteznek. A tudat működik úgy, hogy ezekből az
eloszlásokból konkrét értékeket csinál.” —Vassy Zoltán
Page 84
A programozásról
56 Created by XMLmind XSL-FO Converter.
Az [ORCH OR] modell szerint a mikrotubulus tubulin dimerek alkotta hexagonális rácsa bizonyos
periódusokban klasszikus sejtautomataként végez számításokat, aminek során a rács egy csomópontjának
állapota 6 szomszédjának állapotától függ. Az ezzel szembeni fejlődési periódusokban viszont a rács egyre több
csomópontja kapcsolódik be egy komplex szuperponált kvantummechanikai állapotba, mely során a csomópont
állapotáról nem beszélhetünk, hanem csak azután, amikor ennek a fejlődési folyamatnak a Neumann féle 1-es
folyamat ([NEUMANN KVANTUM KÖNYV] 200. oldal, vagy ismeretterjesztő szinten a [CSÁSZÁR
KÖNYV] könyv - 276. oldal - terminológiájával az R, az állapotredukció folyamata), azaz a mérés véget vet és
a csomópontok a méréskor szóba jöhető valószínűségekkel az egyik vagy a másik állapotukba kerülnek. Az
Penrose-féle OR, objektív redukció szerint a kvantummechanikai unitér fejlődésnek végetvető mérés objektíven
- például a megfigyelő tudatától függetlenül - következik be, amikor a komplex szuperponált állapotban együtt
élő konformációk térbeli tömegeloszlásainak különbsége átlép egy, a Penrose által egy graviton határnak
nevezett értéket. Az [ORCH OR] cikkben ismertetett kalkulációk alapján a szerzők szerint 109 tubulin 500
miliszekundumos összefonódása kiválthatja az egy graviton határ elérését, azaz az objektív redukció
bekövetkeztét. Vagy ugyanígy 1010 tubulin 50 miliszekundumos összefonódása , s így tovább. Ugyancsak az
említett cikkben hivatkozott adatok szerint neurononként 107 tubulinnal számolva az objektív redukcióhoz így
109 / 107 = 100 neuron szükséges, vagy ha a neuron tubulin állományának csupán 1 százalékával - mint a
folyamatba bekapcsolódó tubulinokkal számolunk, akkor 109 / 105 = 10000 neuron fél másodperces
összefonódása szükséges. Ezek alapján a tudat megjelenését Hameroff és Penrose az [ORCH OR TUDAT]
cikkben valahol a C. elegans környékén húzzák meg, mert ennek a fonalféregnek 302 neuronja van [C.
ELEGANS], akár már képes lehet az objektív redukció futtatására.
Az alábbi szimuláció vagy pontosabban csupán - az [ORCH OR], [ORCH OR TUDAT] cikkek képei alapján
készített - demonstráció azt mutatja, hogy egyre több tubulin dimer kerül koherens állapotba, míg az objektív
redukció következtében egy előre nem kiszámítható módon ugranak a résztvevő cellák valamely lehetséges
állapotukba, az ehhez az állapothoz tartozó valószínűséggel.
1.30. példa - Mikrotubulus sejtautomata szimuláció
Ebben a pontban csupán a megfelelő szimulációs, vagy inkább csak demonstrációs programunk által generált
néhány pillanatfelvétel képet mutatunk be, a program részletes feldolgozását a Biológiai témájú programok
című pontban adjuk meg. A programot az [ORCH OR] cikkben közölt képek mintájára készítettük el. A képek
azt mutatják, hogy a hexa rácson egyre több tubulin dimer kapcsolódik be a közös kvantum szuperponált
állapotba. Egészen addig, amíg a bekapcsolódottak kvantum szuperponált állapotában együtt élő konformációk
tömegeloszlásainak különbsége el nem ér egy határt, amikor is bekövetkezik az állapot redukciója, azaz a hexa
rács rácspontjai megint egy meghatározott állapotba kerülnek és indul a sejtautomata klasszikus üzeme, s így
tovább.
1.5. táblázat - Mikrotubulus sejtautomata szimuláció.
Page 85
A programozásról
57 Created by XMLmind XSL-FO Converter.
Page 86
A programozásról
58 Created by XMLmind XSL-FO Converter.
A bekapcsolódottak kvantum szuperponált állapotában
együtt élő konformációk tömegeloszlásainak
különbsége elérte az egy graviton szintet.
Bekövetkezett az állapot redukció.
1.1.5.2. Kvantum számítások
„Hol van mindenki?” —Enrico Fermi
Terjedelmi okokból a kézikönyvben ezt a témát nem bontjuk ki, de fontossága miatt adunk egy rövid irodalmi
útmutatást.
A kvantum számítógép elképzelést bevető alapcikk David Deutsch 1985-ben megjelent [DEUTSCH
KVANTUMGÉP CIKK] cikke. Mára a téma igen kiterjedt. A jelenlegi matematikai háttér megértéséhez
elegendő a Hilbert-terek ismerete, ami a mai legtöbb, nem ismeretterjesztő könyv olvasásához is szükséges. (Itt
érdekességként jegyezhetjük meg, hogy a kvantummechanika matematikai alapjait éppen Neumann János fogta
egybe precíz matematikai szigorral a [NEUMANN KVANTUM KÖNYV] könyvében. Sajnos a programozók
Page 87
A programozásról
59 Created by XMLmind XSL-FO Converter.
reguláris képzése nem vértezi fel a hallgatókat azokkal a matematikai ismeretekkel, amik e könyv élvezéséhez
szükségesek lennének.)
A kvantum számítási téma megismerésére a Debreceni Egyetem Elméleti Fizika Tanszék vezetőjének tollából
származó [KVANTUMINFORMATIKA] jegyzetet ajánljuk.
A programozó Olvasóknak a [PROGRAMOZÓ PÁTERNOSZTER JEGYZET] jegyzetet ajánljuk, ahol két rés
kísérlettől indulva említjük a fontos kísérleti mérföldköveket és részletesen bemutatjuk a legfontosabb kvantum
algoritmusokat.
1.1.5.2.1. Pesszimista programozó optimista gondolata
Futurisztikus fejezetünk lezárásaként játsszunk el a korábbi, mottóként idézet Vassy gondolat [VASSY
JEGYZET] és Neumann utolsó munkájának utolsó oldalain megjelenő gondolat [GÉP és AGY]
összefésülésével! Az első így hangzott
„Így bármilyen furcsán hangzik, logikailag lehetséges, hogy magában az objektív világban a
fizikai mennyiségeknek soha nincs egy-egy jól definiált értékük; csak egy-egy eloszlásuk van,
amelyben a lehetséges értékek egyidejűleg léteznek. A tudat működik úgy, hogy ezekből az
eloszlásokból konkrét értékeket csinál.” —Vassy Zoltán
a második pedig a következő
„Szembe kell néznünk azzal, hogy a nyelv messzemenően történelmi esetlegességet alkot. Az
alapvető emberi nyelvek különböző formái hagyományszerűen jutottak el hozzánk, de már e
hagyományos formák sokfélesége is bizonyítja, hogy semmiféle feltétlenség vagy
szükségszerűség nem testesül meg bennük. S mint ahogy a görög vagy a szanszkrit nyelv
létezése történeti tény, nem pedig feltétlen logikai szükségszerűség, ugyanúgy józanul
feltételezhetjük, hogy a logika és a matematika is történeti eredetű és esetleges kifejezési
formák. Lehetnek a logikának és a matematikának lényegesen eltérő változatai is - mindkettő
más alakban is létezhetik, mint amit megszoktunk! ” —Neumann János
Honnan a pesszimizmus? Mert, ha a tudatunk is csupán történeti esetlegesség, akkor Fermi kérdésére a válasz,
hogy sehová nem tűntek, mert számunkra - abban az értelemben, melyben a kérdés fel lett téve - nem is
léteznek! Tehát elvben jóval könnyebb lenne olyan fordítóprogramot írni, ami a delfin-ember vagy a fonalféreg-
ember kommunikációt teszi lehetővé, semmint a „zöld fickók” - ember fordítót. Ha ez a pesszimista helyzet
állna fent, akkor csak abban bízhatunk, hogy a kvantuminformatikus programozók majd kifejlesztik a megfelelő
fordítót! De addig is a mi életünk csíráit küldenénk ki a világegyetembe, hogy idővel majd találkozhassunk
olyanokkal, akikkel kölcsönösen létezünk egymás számára.
1.1.5.3. Visszabeszélő gépek
Még a tanár-diák kontextusban kezdődött el a következő, idézett kérdés a kézikönyv szerzőpárosa között.
„Miért lenne az jó, ha az asztalon lévő gép visszaszólna?” Adekvát válasz még ma sincs, hanem csak ebbe
tudományos fantasztikus ihletésű részben merjük beírni, hogy „Mert az intelligens gépek azt is el tudják majd
képzelni, amit mi emberek már nem.”
1.2. Az OO világ, a programozó szűkebb hazája
„Több erőfeszítést fektetünk a tervezésbe és kódolásba (ami szórakozás), hogy csökkentsük a
jóval költségesebb tesztelést és hibajavítást (ami nem az).” —Kernighan-Plauger A programozás magasiskolája
Ha futása közben - egy koncepcionálisan elég magas nézőpontból - rápillantunk egy OO programra, akkor
egymással kölcsönható, együttműködő objektumokat látunk. Amit a nyolcvanas, kilencvenes évek tipikus
imperatív programozója gondolt egy egész programja futásáról: szépen szekvenciálisan megy a végrehajtás, jön
egy szelekció, azon az ágon megyünk tovább... igen, itt akkor következik egy iteráció... átkerült az
objektumokba. Ezért OO programra az esetleges jól olvasható jelző helyett szerencsésebb, találóbb lehet az
érthető világ, jól megérthető világ leírás jellemzés. Mert az OO programozó építette osztályoknak arra a
Page 88
A programozásról
60 Created by XMLmind XSL-FO Converter.
kihívásra kell válaszolniuk, hogy modellezni tudják-e a programozó előtt álló feladat világát? Ha igen, akkor a
feladat sikeres megoldása annyit tesz, hogy a modellezett világban megnézzük a választ a kérdéseinkre és a
válaszokat a valóságos világbeli válaszoknak tekintjük.
Például ha a következő ábrán bepillantunk az Órarend programba, akkor az azt mondja, hogy Bátfai Norbert
Linuxot és Operációs rendszereket fog tanítani. Illetve ugyanez a valóságban: a közelgő félév elején BN
megnézi majd ennek a programnak a grafikus felületét és onnan tudni fogja, hogy talán már éppen másnap
hányra és melyik terembe kell mennie megtartani melyik kedvenc óráját.
Az OO programozó tehát osztályokat épít, olyan osztályokat, amik lehetőség szerint jól modellezik a
megoldandó feladat világát. Ennek során a programozó tipikusan felhasznál már rendelkezésre álló osztályokat.
Az osztályoknak azt a körét, amik minden ilyen építkezésnél alapvető fontosságúak, illetve adott részirányokban
(ilyen részirány lehet - csak néhány példát megemlítve - a mobil, az adatbázis, a hálózati, az elosztott, a
konkurens, a web programozás) alapvető fontosságúak lehetnek, Alkalmazói Programozói Interfésznek, röviden
API-nak nevezzük. Így nem meglepő, hogy pusztán az OO nyelv ismerete az API nélkül mit sem ér!
Mint az ember minden bonyolult dolgot, az API-t is hierarchiába szervezi, esetünkben ez a hierarchia egy fa
szerkezet, aminek gyökere a Java. A fa leveleit nem tartalmazó utakat csomagnak is nevezik, a következő ábra
alapján például a java.lang egy csomag. A java.lang csomag azokat az osztályokat tartalmazza amik a nyelv
szempontjából alapvetők. A String osztálybeli objektumok karakterláncokat reprezentálnak, ezek fontosságát a
programozásban nem kell tovább ecsetelnünk, mint ahogyan a számok fontosságát sem: az Integer osztály
példányai például az egész számokat reprezentálják. Kevésbé egyértelmű az Object osztály, pedig - ahogyan
majd hamarosan látjuk - ő a legfontosabb, ő az osztályhierarchia gyökere, azaz minden Java osztály őse... De ne
aggódjon a kedves Olvasó, az API-t még véletlenül sem kell megtanulni, hanem csak nézegetése közben
hozzászokni, mert természetesen a platform azért platform, hogy biztosítson olyan eszközöket, amik lehetővé
teszik, illetve segítik a fejlesztést, esetünkben bemutatják az API hierarchiát. De távolodjunk el pár bevezető sor
Page 89
A programozásról
61 Created by XMLmind XSL-FO Converter.
erejéig most a fáktól és nézzük az erdőt. A fákhoz majd a következő néhány pont után, a Java SE OO világ című
pontban térünk vissza.
1.2.1. A Java platform
1. Mit gondol a kedves Olvasó, találkozott már valaha a Javával?
2. Van mobilja?
3. Igen, akkor az első kérdésre a válasz: biztosan! Hogy miért? Mert a telefonja nagy valószínűséggel Java
képes készülék.
Mi tehát a Java? A válasz attól függ, ki kérdezi. Mert mondjuk a földrajzosoknak lehet egy sziget az Indiai-
óceánon. A programozóknak lehet egy modern programozási nyelv, megélhetés vagy szerelem... Az általában
vett mobil tulajdonosnak, azaz most nekünk, egy program a telefonon: egy számítógépes program. Ha eddig
nem tekintettük számítógépnek is a mobilunkat, akkor kezdjünk barátkozni ezzel a gondolattal, mert bár nem
olyan erős sebességben, a kijelzője méretében vagy mondjuk memóriakapacitásában, mint az asztalainkon
használt, mára már jól megszokott személyi számítógépeink, de mégiscsak, számunkra most számítógép.
Visszatérve a Javahoz, ennek a programnak az a feladata, hogy más, éppen Java nyelven megírt programokat
futtasson, ezért is nevezik egyszerűen csak Java Virtuális Gépnek, vagy röviden JVM-nek ezt a futtató
programot. Tehát, ha például egy telefon javas, akkor az annyit tesz, hogy rajta van a Java VM program.
Jogos a kérdés, miért hasznos ez nekem mint mobiltelefontulajdonosnak? Mert a JVM elrejti a mobiltelefonok
sokféleségét azzal, hogy a fejlesztőknek elég a JVM-re megírni a programjaikat, azok pedig automatikusan
működni fognak minden JVM-el rendelkező, azaz Java képes mobilon is! Tehát végső soron én mint mobil
tulajdonos elvben több programhoz juthatok hozzá, többhöz, jobbhoz, gyorsabban, olcsóbban. Összefoglalva, a
Java erejét a mobiltelefonok világában (azon túl, hogy a Java az Java :) az adja, hogy az elkészített programom
elvben automatikusan minden Java képes eszközön működni fog. Ezt a tulajdonságot nevezzük
platformfüggetlenségnek: a Java programom futni fog mindenféle javas mobiltelefonon: Motorolán, Nokián,
Sony Ericssonon... vagyis minden olyan telefonon, amin rajta van a Java VM!
A mobilokról általánosítva a személyi számítógépekre, a szerver számítógépre, az egész internetre ugyanez adja
a Java erejét: a programozó egy nyelvet megtanulva, egy nyelvet használva tudja a legkülönfélébb
programozható eszközöket, például mobilokat, személyi számítógépeket, szervereket kezelni, anélkül, hogy
magukkal ezekkel a különböző hardver eszközökkel, vagy az eszközökön található szoftveres erőforrásokkal,
Page 90
A programozásról
62 Created by XMLmind XSL-FO Converter.
például a futó operációs rendszerekkel: Linux-szal, Windows-zal vagy Solarisszal foglalkoznia kéne.
Az ábra beszédes, de hogy pontos is legyen, még finomítanunk kell: a Java Virtuális Gépek bemenete nem
közvetlenül az emberi fogyasztásra alkalmas Java forrásszöveg, hanem egy olyan köztes forma, amit egy
fordítóprogrammal, a javac nevű Java fordítóprogrammal készítünk el. Ez már nem emberi fogyasztásra
alkalmas, szöveges állomány, hanem bináris. Ez a virtuális gépek bemenete, ez az a hordozható forma, amit a
Java Virtuális Gépek értelmeznek. Egészen konkrétan, ha készítek egy ilyen, mondjuk Java SE platformbeli
osztályt (programot) Linux alatt, amit most nevezzünk Osztály.class osztálynak, akkor egyszerűen egy
Windows rendszerű gépre átmásolva, ott átadva az ottani Java Virtuális Gépnek, azaz például a megfelelő
parancssorba beírva a java Osztály parancsot, futni fog a programom. A Java telepítés és futtatás kapcsán
lásd még A Java telepítése gépünkre és A példaprogramok futtatása című pontokat!
1.2.1.1. Az első Java osztály lefordítása
Függessze most fel a kedves Olvasó az olvasást és fordítsa, majd futtassa le első Java programját. Ehhez először
persze gépünkre installálnunk kell a megfelelő fejlesztői környezetet. Ezt sikeresen megtehetjük A Java
telepítése gépünkre és A példaprogramok futtatása című pontok alapján, tegyük hát most meg!
Page 91
A programozásról
63 Created by XMLmind XSL-FO Converter.
A kézikönyv eddigi olvasása során már sok Java nyelvű forrás programot láttunk, bármelyikkel
megpróbálkozhatunk most, mint első fordításunkkal, futtatásunkkal. De mi az alábbi, a Fibonacci sorozat
(amelyben a sorozat adott tagja az őt megelőző két tag összege) tagjait kiszámító program fordítását javasoljuk.
A program kódjára magára egyelőre csak egy pillantást vessünk, mert fő feladatunk most a fordítás, futtatás
folyamat technikai bemutatása.
public class Fibonacci {
int előző;
int aztMegelőző;
public Fibonacci() {
előző = 1;
aztMegelőző = 1;
}
public int következő() {
int vissza = előző + aztMegelőző;
aztMegelőző = előző;
előző = vissza;
return előző;
}
public static void main(String[] args) {
Fibonacci finobacci = new Fibonacci();
for(int i=0; i<10; ++i) {
System.out.print(finobacci.következő());
System.out.print(" ");
}
}
}
Vágjuk ki a fenti kódot, majd illesszük be a Fibonacci.java nevű állományba. Végül fordítsuk, majd futtassuk
az alábbiak szerint, például Linux alatt így:
[norbi@niobe ~]$ javac Fibonacci.java
[norbi@niobe ~]$ java Fibonacci
2 3 5 8 13 21 34 55 89 144
1.2.1.1.1. Bitfaragó feladat
Milyen bitmintával kezdődnek a Java bájtkódok, azaz a javac nevű Java fordító generálta .class kiterjesztésű
állományok? Ha a választ hexában nem találjuk kapcsolatosnak a Java gőzölgő kávéscsésze logójával, akkor
még dolgozzunk rajta! Nézzük meg például az imént elkészített Fibonacci.class állományt!
Tipp
Ha már nagyobb gyakorlattal rendelkezünk, akkor a 0,1 feladat feladata is segíthet.
1.31. példa - Java disassembler
Page 92
A programozásról
64 Created by XMLmind XSL-FO Converter.
Az előző feladatot nyilvánvalóan azonnal megoldotta az a kedves Olvasó, aki megnézte hexadecimálisban a
Fibonacci.class állományt. A Java fejlesztői környezet egyébként ad kényelmesebb eszközt, hogy a
bájtkódot nézegessük. Bár erre az eszközre az átlagos Java fejlesztőnek valószínűleg nem lesz szüksége, de
megemlíteni mindenképpen érdekes. Ez a program a javap, feladata a bájtkódból az assembly nyelvek
tokenjeihez hasonló kimenetet előállítani. A -c kapcsolóval indítva bemenetként a Fibonacci.class bájtkódot
átadva beszédesen mutatja be a Java bájtkódot.
[norbi@niobe ~]$ javap -c Fibonacci
Compiled from "Fibonacci.java"
public class Fibonacci extends java.lang.Object{
int előző;
int aztMegelőző;
public Fibonacci();
Code:
.
.
.
public int következő();
Code:
0: aload_0
1: getfield #2; //Field előző:I
4: aload_0
5: getfield #3; //Field aztMegelőző:I
8: iadd
9: istore_1
10: aload_0
11: aload_0
12: getfield #2; //Field előző:I
15: putfield #3; //Field aztMegelőző:I
18: aload_0
19: iload_1
20: putfield #2; //Field előző:I
23: aload_0
24: getfield #2; //Field előző:I
27: ireturn
public static void main(java.lang.String[]);
Code:
.
.
.
Csupán a következő() függvény bájtkódját emeltük ki a javap által készített kimenetből. Ami bár emberi
fogyasztásra nem oly alkalmas, mint következő() metódus Java forráskódja, de azért próbáljuk összevetni a
kettőt!
1.2.1.2.
Futtatható megjegyzés
Bár nem javasoljuk, de lehetőség van a Java forrásból futtatható állomány létrehozására is, az ez iránt
érdeklődőknek a [PROGRAMOZÓ PÁTERNOSZTER JEGYZET] jegyzetre hívjuk fel a figyelmét,
ahol Linux alatt a GNU gcc fordító használatával mutatunk erre példákat. Miért nem javasoljuk? Első
ok, hogy így elveszítjük a hordozhatóságot, a második, hogy ezt a tettet általában a sebesség kérdésben
sem tartjuk indokoltnak. A sebesség kapcsán érdekes lenne írni valamilyen benchmark programot,
hogy magunk is meggyőződjünk erről. Írjunk is! A Pi jegyeinek nyomában című pont BBP algoritmust
megvalósító PiBBP osztályunkat átírtuk objektumorientálttalanná, azaz gyakorlatilag nem készítettünk
benne objektumokat, hanem csak egy statikus main() függvényt használtunk, illetve az algoritmust
átírtuk C nyelvre is. Ekkor az alábbi futási eredményeket kaptuk az alábbi egyszerű mérésekkel
Javaban
Page 93
A programozásról
65 Created by XMLmind XSL-FO Converter.
long delta = System.currentTimeMillis();
/* SZÁMÍTÁS */
delta = System.currentTimeMillis() - delta;
System.out.println(delta/1000);
illetve C-ben
clock_t delta = clock();
/* SZÁMÍTÁS */
delta = clock() - delta;
printf("delta: %f\n", (double)delta/CLOCKS_PER_SEC);
a Pi hexadecimális kifejtésének (0. pozíciótól számított) adott poziciója hexa jegyének
meghatározására egy Fedora Linux Core 5 operációs rendszerrel felszerelt, 2.6.17.7 verziójú
kernellel ellátott Sun W1100Z Workstation gépen gcc (GCC) 4.1.0 20060304 (Red Hat 4.1.0-
3) verziójú gcc és java version "1.6.0-beta2" verziójú Java mellett. (A szóban forgó méréseket
végző, említett teljes programokat, illetve néhány további összehasonlítást a melléklet Egyszerű
összehasonlítások a sebesség kérdésében című pontjában találhat az érdeklődő Olvasó.)
1.6. táblázat - Java és C egyszerű sebesség összehasonlítása
Pozíció 0xJegy C [sec] Java [sec]
106 6 4.39 4.246
107 7 51.19 49.465
108 C 586.0 556.935
Linux alatt a gcc fordító használatával így készítünk futtatható állományt a fenti Fibonaccis
programunkból:
[norbi@niobe ~]$ gcj -o fibonacci --main=Fibonacci Fibonacci.java
[norbi@niobe ~]$ ./fibonacci
2 3 5 8 13 21 34 55 89 144
Lássuk, hogy valóban futtatható állomány készült
[norbi@niobe ~]$ file fibonacci
fibonacci: ELF 64-bit LSB executable, AMD x86-64, version 1 (SYSV),
for GNU/Linux 2.6.9, dynamically linked (uses shared libs), for GNU/Linux 2.6.9,
not stripped
szemben a bájtkóddal:
Page 94
A programozásról
66 Created by XMLmind XSL-FO Converter.
[norbi@niobe ~]$ file Fibonacci.class
Fibonacci.class: compiled Java class data, version 50.0
1.2.1.3. Történet és oktatás
„A jövőt nem lehet megjósolni. Azt föl kell találni.” —Gábor Dénes
Patrick Naughton, Mike Sheridian és James Gosling részvételével 1990 végén, 91 elején mindenféle
potenciálisan új generációs informatikai termékek kifejleszthetőségének vizsgálatával indult meg egy
technológia-intenzív csoport, a Green Team munkája. A háztartásokban található mindenféle különböző
hardverű szórakoztató elektronika vezérlése, programozása akkor ilyennek tűnt.
Az időközben személyi állományban tucatnyira növő projekt 1992 szeptember harmadikán bemutatta az általuk
kifejlesztett termék prototípusát a *7-et, azaz a Csillag7-re keresztelt 5 colos érintőképernyős, vezeték nélküli
hálózati kapcsolattal ellátott PDA-t. A *7 feladata az volt, hogy vezérelni tudja az olyan szórakoztató
elektronikát, ami érti, interpretálja az ehhez kifejlesztett új nyelvet, az Oakot. (Az Oak, Tölgy név eredete, hogy
Gosling irodájának ablaka éppen egy ilyen fára nézett.)
A sikeres demó után a termék értékesítésére alakult a csoportból a FirstPerson, de a piac még nem állt készen.
Végül az interpretált Oak nyelv, 1995-ben, már mint Java jelent meg, a korábban megcélzott piac helyére pedig
a forradalomként terjedő internet került. A Java születési ideje: 1995. május 23, amikor a SunWorld
konferencián John Gage, a Sun kutatási igazgatója hivatalosan bemutatta a Javát és az azt értő HotJava
böngészőprogramot, továbbá a konferencián a Netscape bejelentette, hogy böngészőjében támogatni fogja a
Javát.
Véleményünk szerint ez a szerencsés Java+internet összefonódás is szerepet játszott abban a folyamatban, ami a
programozó közösség javát abba az irányba mozdította, hogy a Java nyelv átvegye az első számú általános
programozási nyelv helyét az elméjében. Mert egy új programozási nyelv elsajátítása komoly mentális
befektetést igényel, de az már egy egészen más dolog, ha ez a befektetés az új világhoz, azaz akkoriban az
internet programozásának világához is megadja a kulcsot. Nem befolyásolja a homokvihart a porszem
véleménye, de magunk annak szorítunk, hogy a Java nyelv napjaink római békéje legyen a programozásban.
Az oktatás területén - az elmúlt 15 évben - két birodalom tündöklését és bukását éltük meg, a ma harmincas
éveikben járó programozók még diákként a BASIC-ét, majd hallgatóként a PASCAL-ét. A hanyatlás oka
magának a nyelvhez kapcsolódó kultúrának, magának a nyelvhez tartozó civilizációnak a hanyatlása. Mert csak
az oktatás ezeket a mentális birodalmakat nem tarthatja fenn, mivel a tipikus tanuló a nyelvhez kapcsolható
kultúrához, civilizációhoz kötődik és nem csupán a tiszta nyelvhez, gondolkodásmódhoz magához. Ezért az a
meggyőződésünk, hogy amit az oktatási célokra szánt nyelvekkel alapelvekben meg lehet tanítani, azt már
korábban, a 10 éves kor környékén meg lehetne és meg kell majd tanítani mondjuk a LEGO Mindstorms™
robotos eszközök segítségével. Mert ez a robotos készlet az életkorban ide kapcsolható játékkultúrához,
gyermeki - építő játék - civilizációhoz kapcsolódik, s ennek a civilizációnak a fennmaradása hosszabb távon is
bíztató.
A LEGO Mindstorms Robotics Invention System csomag 1998-ban jelent meg. Ez alapvetően egy több száz
alkatrészből álló LEGO csomag, de több speciális téglát is tartalmaz. Legfontosabb ilyen a processzort is
tartalmazó tégla, de vannak motorokat tartalmazó, nyomás és fény érzékelésére alkalmas téglák is a csomagban.
Page 95
A programozásról
67 Created by XMLmind XSL-FO Converter.
A robot a szokásos építkező játékkal készíthető el, például az iménti ábra a RobIGOR névre keresztelt, a II.
Jávácska konferencia [II. JÁVÁCSKA] kabala LEGO robotját mutatja. Az robot megépítését egy PC-n történő
programozási rész követi. A programot grafikus környezetben készíthetik el a gyerekek, majd infra kapcsolaton,
illetve az új csomagban már Bluetooth kapcsolaton vagy USB kábelen keresztül küldhetik át a robotra, hogy
aztán azon futtathassák. A csomag új generációs változata, a LEGO Mindstorms NXT [LEGO
MINDSTORMS], 2006 elejétől elérhető.
De visszatérve a Java történelemhez, az időtengely a másik irányban érdekesebb: a közeli múlt (2004 vége,
2005 eleje) a Java 5, a Tiger. A jelen (2006 nyara) a Java 6, a Mustang. A közeli jövő (2008 második fele) a
Java 7, a Dolphin. Az appendix A Java verziók újdonságai című pontjában bontunk ki majd néhány
szívdobogtató finomságot, amiket majd rögtön alkalmazunk is labirintus példáinkra.
1.2.1.4. A Java OO világ
Már bevezettük: a Java OO világ térképe az API dokumentáció, az osztályok részletes leírásának egy fa
szerkezetbe rendezett rendszere. Érdekessége, hogy a programozás során a programozó által a forrásszövegbe
illesztett dokumentációs megjegyzésekből áll össze böngészhető HTML formátumban. A dokumentációs
megjegyzéseket a /** karakterek kezdte és a */ befejezte részben kell elhelyezni, amiként a következő példa is
mutatja, hogy hogyan kommentezzük be egy osztályunkat, az osztály példányváltozóit és a függvényeket. Ha a
program fejlesztése, írása közben a dokumentálást nem hanyagoljuk el, akkor amikorra a programunk kész,
akkorra egy jól használható dokumentáció is kész.
/**
* A labirintus kincseit jellemző osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.1
* @see javattanitok.labirintus.Labirintus
*/
public class Kincs extends Szereplő {
/** A kincs értéke. */
protected int érték;
/** Megtaláltak már? */
protected boolean megtalálva;
/**
* Létrehoz egy {@code Kincs} objektumot.
*
* @param labirintus amibe a kincset helyezzük.
* @param érték a kincs értéke.
*/
public Kincs(Labirintus labirintus, int érték) {
super(labirintus);
this.érték = érték;
}
Page 96
A programozásról
68 Created by XMLmind XSL-FO Converter.
A forrásokból Java platform javadoc nevű programja készíti el a böngészhető dokumentációt a fejlesztett
programunkról, de természetesen a NetBeans-ben is kattinthatunk a: Bulid/Generate Javadoc for... menüpontra.
A NetBeans kapcsán lásd még a A NetBeans integrált fejlesztői környezet használata című pontot!
A bal felső
keretben az osztályokat találjuk, ha kattintással kiválasztunk egyet, akkor ennek a csomagnak az osztályai,
interfészei, kivételei a bal alsó keretben bontódnak ki. Ha itt megintcsak kiválasztunk egy tételt, esetünkben
például a Hős osztályt, akkor annak részleteit a fő frame-ben ismehetjük meg.
Page 97
A programozásról
69 Created by XMLmind XSL-FO Converter.
De a Java dokumentáció erejét a következő pontban csodálhatjuk meg igazán.
1.2.1.5. Java SE OO világ
Akkor hát csodáljuk: a http://java.sun.com szájtról letölthető Java SE fejlesztői csomag, teljes nevén a Java
Platform, Standard Edition 6 Development Kit, JDK 6 - most a 6-os verziónál tartunk, mindig figyeljünk, hogy
a legújabbat használjuk! Immár két éves az 5-ös Java, a Tigris, most aktuális Java a 6-os, a Musztáng, de már
várjuk a 7-es Javát, a Delfint. A letöltéssel és a telepítéssel kapcsolatos további részletek tekintetében lásd A
Java telepítése gépünkre című pontot!
Visszatérve a csodáláshoz: a sikeres telepítés után a JDK könyvtárában találhatjuk az src.zip nevű állományt,
amiben megtaláljuk a Java SE OO világ (azaz a Java SE API) összes osztályának forráskódját. Ha nem csupán a
JDK-t töltöttük le, hanem a mellette felkínált dokumentációt is, akkor láthatjuk, hogy ez a dokumentáció éppen
ezekből a forrásokból készült, főleg az imént bemutatott dokumentációs megjegyzések felhasználásával. (Azért
érdemes a JDK mellől rögtön letölteni a dokumentációt is, mert így biztosan a megfelelő verziót használjuk
majd, a letöltés tekintetében lásd a A Java dokumentáció, azaz az API doksi letöltése című pontot!)
1.2.1.5.1. System.out feladat
Nézzük meg, hogy milyen osztálybeli objektum a System osztályban lévő out tag! A választ az imént említett
src.zip java/lang/System.java forrásában találjuk. Ha megvan, majd nézzük meg az API doksiban, azaz
szintén az imént említett, a JDK mellől letölthető dokumentációt is: a bal felső frame-ben java.lang csomagra
kattintás után a bal alsóban Classes-ok alól a System osztályra kattintva a jobb oldali fő frame-ben már
megtaláljuk. Ez utóbbit azért egy képen is megmutatjuk:
A Java SE API már hatalmas, ezért itt nem is teszünk mást, csak néhány olyan csomagjáról szólunk pár szót,
amelyet a későbbi példáink során mi magunk is használni fogunk.
Már tárgyaltuk, hogy az ember mint minden bonyolult dolgot, az API-t is hierarchiába szervezi. Megnéztük,
hogy esetünkben ez a hierarchia a fa szerkezet, aminek gyökere a java. A fában található, a gyökérből kiinduló
utak pedig a csomagok.
A java.lang csomag tartalmazza az Object osztályt, ez az osztály az öröklődés alapján tekintett
osztályhierarchia gyökere, avagy minden Java osztálynak ez az ős osztálya: apja, nagyapja, dédnagyapja,
üknagyapja stb. Így minden osztály örökli például a getClass() metódusát. A getClass() metódus
megmondja az objektumról, hogy mely osztálybeli. (A részleteket gyakorlásképpen nézze meg a kedves Olvasó
az API doksiban!) Lássunk egy példát a metódus használatára:
Page 98
A programozásról
70 Created by XMLmind XSL-FO Converter.
Labirintus labirintus = new Labirintus(6, 3);
Hős hős = new Hős(labirintus);
System.out.println(labirintus.getClass());
System.out.println(hős.getClass());
A kódrészlet produkálta output:
class javattanitok.labirintus.Labirintus
class javattanitok.labirintus.Hős
Magunk is próbálkozhatunk, a fenti kódrészletet a javattanitok.labirintus.Labirintus saját indító main()
metódusában találjuk meg.
1.2.1.6. Java ME OO világ
Egy magára valamit is adó mobilt árusító üzletben vagy mobilos katalógusban
ma már nem csupán azt láthatom a készülékek mellett, hogy Java képesek-e, hanem további lényeges
finomságokat is, például MIDP 1.0-ás vagy MIDP 2.0-ás a kiszemelt készülék? Ha megkérdezzük, hogy ez a
rövidítés mit jelent, akkor a választ az Java ME OO világban találjuk. Ebben a világban is minden objektum, így
már nem lehet meglepő, hogy programjaink itt is objektumokból állnak.
Nézzük meg mobilunkon a jegyzetben fejlesztett labirintus programot: egy elegendően magas koncepcionális
nézőpontból éppen egy vászon objektumot látunk. A vászon objektumok arra valók, hogy rajzolni tudjunk a
mobilunk kijelzőjére, most éppen a labirintusunkat láthatjuk rajta. Ha például azt szeretnénk, hogy a szereplők,
mondjuk a hős és a szörnyek valóságosabban mozogjanak, azaz lépkedjenek, akkor a vásznon további
objektumokat, sprite objektumokat is kellene használnunk. A sprite objektumok arra valók, hogy mozgó
dolgokat, például a szereplők animált mozgását készítsük el velük. A mozgások tipikusan animációkból állnak
össze, azaz lerajzoljuk a lépéseket mondjuk 8 fázisban és minden egyes fázist tartalmazó kép a sprite
animációjának egy frame-je lesz... egyelőre ne szaladjunk ennyire gyorsan!
Számtalan rendeltetésű objektumról beszélhetnénk még, de távolodjunk el a fáktól és nézzük az erdőt! A
telefonokon rendelkezésre álló objektumok összességét szabványok foglalják össze, ilyen szabvány a MIDP is.
A mobil téma gyors fejlődése persze magával hozza, hogy idővel egyre több objektum áll rendelkezésre, azaz
mind több és több objektummal gazdagodik ez a világ. Például a sprite objektumok a MIDP 2-ben jelentek meg,
a korábbi MIDP 1-ben még nem voltak, ekkor az animációkkal kapcsolatos apróságokat a programozónak
magának kellett összerakosgatnia, azaz a kép helyére a megfelelő fázist tartalmazó frame-t kirajzolnia.
Page 99
A programozásról
71 Created by XMLmind XSL-FO Converter.
Minden objektumnak megvannak a saját belső tulajdonságai (tagjai, példányváltozói, attribútumai) és az
ezekhez a tulajdonságokhoz tartozó saját viselkedési formái (függvényei, módszerei, metódusai, neki küldhető
üzenetei). Az objektumok viselkedési formáikon keresztül tudják változtatni belső tulajdonságaikat (tehát
változóik értékeit) és viszont: a belső tulajdonságok befolyásolják a viselkedést (metódusainak működését).
Például a mobiltelefonok Java Virtuális Gépében élni képes objektumok a MIDlet osztályból származó
objektumok. A MIDlet objektumok rendelkeznek azokkal a tulajdonságokkal és viselkedési formákkal, amik
lehetővé teszik, hogy a mobiltelefonok Java Virtuális Gépében élni, működni tudjanak. Ennek megfelelően, ha
mobiltelefonos programot akarunk készíteni, azaz olyan osztályt, amelyből példányosított objektumom majd a
mobilon futhat, akkor osztályunkkal ezt a MIDlet osztályt kell kiterjesztenünk, mert ekkor a saját osztályom is
kész lesz arra, hogy ebben a speciális környezetben éljen.
Ugyanezt elmondhatnánk a javax.microedition.MIDlet osztály helyett
i. a java.applet.Applet osztályt helyettesítve, ha ez a speciális környezet a webböngésző
ii. a javax.servlet.http.HttpServlet osztályt helyettesítve, ha ez a speciális környezet a webszerver.
Page 100
A programozásról
72 Created by XMLmind XSL-FO Converter.
A Java ME API, bár nagyságában
korántsem olyan hatalmas, mint a Java SE API, de a MIDP 2 igen jelentős növekedést hozott, ezért itt sem
teszünk mást, csak néhány olyan csomagjáról szólunk pár szót, amelyet a későbbi példáink során mi magunk is
használni fogunk. A java alól nyíló csomagok: a java.lang, java.util és a java.io a Java SE-ből lettek
leválogatva, tehát ezeknek a csomagoknak a tartalmát ugyanígy a Java SE API is tartalmazza. Erre utalva
hagytuk meg a következő ábrán a korábbi hasonló ábra tetejét. A javax alól nyíló csomagok közül csak a
javax.microedition.midlet, javax.microedition.lcdui csomagokat és a
javax.microedition.lcdui.game alcsomagot tüntettük fel.
1.2.1.6.1. MIDlet feladat
Az API doksiban (telepítésével kapcsolatban lásd a A Java dokumentáció, azaz az API doksi letöltése című
pontot) nézzük meg a javax.microedition.midlet csomagban található MIDlet osztály startApp(),
pauseApp(), destroyApp() módszereit! Milyen módosítókkal rendelkeznek?
Ezek a metódusok az absztrakt jelzővel vannak ellátva, ez azt jelenti, hogy ha ezt az osztályt kiterjesztjük, azaz
valamely saját osztályunkat ebből örököltetjük, akkor ezeket a metódusokat mindenképpen, akár üres törzzsel is,
de felül kell definiálnunk. E három metódus a MIDlet osztály életciklus metódusai, velük vezérelhetjük,
segíthetjük a mobilos objektumunkat, hogy meg tudjon élni az idegen környezetben. Mely környezet abban az
Page 101
A programozásról
73 Created by XMLmind XSL-FO Converter.
értelemben idegen, hogy fő célja nem biztos, hogy a mi alkalmazásunk futtatása, mert például programunkat
bármikor félbeszakítathatja egy éppen bejövő hívás vagy SMS. Az alábbi, a
javattanitok.LabirintusMIDlet MIDlet osztályból kiragadott kódrészlet azt mutatja, hogy a pauseApp()
módszert csupán üres testtel definiáltuk felül, ami azt is jelenti, hogy nem akarunk a programunkkal reagálni az
előbb említett, de a mobil platformon természetesen szokásos eseményekre. A startApp() metódusban viszont
dolgozunk, kitesszük a képernyőre labirintusunk már korábban elkészített vásznát.
public void startApp() {
// A kijelzőn a labirintus vászon legyen látható
kijelző.setCurrent(labirintusVászon);
}
public void pauseApp() {
}
A saját MIDlet (a MIDlet osztályból származó, azaz mondhatjuk, hogy MIDlet) osztályunk konstruktorának
lefutása után hívódik meg a startApp() metódus, ezzel a MIDlet objektumunk aktív állapotba került. Ebből az
állapotból időlegesen a pauseApp() módszer viheti ki, véglegesen pedig a destroyApp() módszer. Ennek
megfelelően vegyük figyelembe, hogy a startApp() metódus MIDlet objektumunk (programunk) élete során
többször is lefuthat. Ez konkrétan azt jelentheti - feltéve továbbá, hogy a labirintust és az azt kirajzoló vásznat a
startApp() metódusban hoznánk létre - hogy a játékos már javában labirintusozik, amikor érkezik egy bejövő
telefonhívás: meghívódik a pauseApp(), a játékos felveszi vagy sem, de amikor visszatér a játékhoz, akkor a
startApp() metódus hívódik meg és feltételünk értelmében újra elkészíti a labirintust. Tehát a játékos a régi
játékát gyakorlatilag elveszítette és egy új labirintussal újra kell kezdenie...
Az javax.microedition.lcdui csomag tartalmazza a GUI interfésszel kapcsolatos osztályokat, ezek a
szokásosak: űrlapok, nyomógombok, szövegbeviteli mezők stb. A javax.microedition.lcdui.game
alcsomag már kimondottan a játékfejlesztőket segíti. Többek között itt találhatjuk a Sprite osztályt is. A
GameCanvas egy olyan vászon, amiben egyben a játék időfejlődését is meg tudjuk adni. A későbbiek során erre
majd látjuk a példaprogramot: a LabirintusMIDlet és a LabirintusVaszon osztályainkat.
1.2.2. Objektumok mindenütt: a CORBA OO világ
A CORBA programozó a napjainkban elérhető legmagasabb szoftveres absztrakciós szinten dolgozik. Számára
az objektumok közötti kapcsolattartás immár transzparens. Ha a fejlesztő valamely CORBA objektum
szolgáltatását szeretné igénybe venni, azaz az objektum valamely metódusát meghívni, akkor csupán a CORBA
objektum referenciáját kell megszereznie és innentől a megszokott módon a kívánt szolgáltatásnak megfelelő
metódusát meghívnia. Megfordítva, ha a programozó egy szolgáltatást kíván nyújtani, akkor elég csupán a
szolgáltatásra magára koncentrálva megírnia a megfelelő CORBA objektumot. Mindkét esetben csakis a
szolgáltatásra kell koncentrálni, akár igénybe venni, akár szolgáltatni akarjuk azt. Hogy az objektumok
fizikailag hol vannak, az nem lényeges. Sőt még az sem lényeges, hogy ezeket a CORBA objektumokat milyen
programozási nyelven implementálja a fejlesztő. Természetesen mi a legjobb választással élve, Java nyelven
valósítjuk majd meg a példaként szolgáló CORBA objektumainkat.
Page 102
A programozásról
74 Created by XMLmind XSL-FO Converter.
Hogyan érjük el a szoftveres absztrakció ilyen magas fokát? Úgy, hogy a CORBA világában szereplő
objektumokat mindig egy interfészen, az objektum IDL (Interface Definition Language)interfészén keresztül
látjuk. Olyannyira mindig, hogy már magának a CORBA alapú rendszernek a tervezésekor is ezekben az
interfészekben gondolkozunk. Mert a CORBA objektumoknak olyan tulajdonságaik és viselkedéseik vannak,
amiket ezek az interfészek specifikálnak. Praktikusan egy CORBA objektum ORB brókeren keresztül elérhető
módszerei az objektum IDL interfészében definiált módszerek. További tárgyalásunkban a Java keretein belülre
szorítkozva: a Java platform biztosít olyan eszközöket - például az idlj fordítót - amik az IDL interfész
definíciókat Java nyelvre fordítják. Ezután nincs más dolgunk, mint az IDL interfészben megnevezett
metódusok - esetünkben Java nyelvi - megvalósítása.
Példaként nézzük meg, hogy CORBA labirintusos esettanulmányunkban miként fogalmazzuk majd meg, hogy a
szereplők a labirintus valamely oszlopában és sorában vannak. A labirintus hőse a labirintus egy olyan
szereplője, aki gyűjtögeti a labirintusban megtalált értékeket, van valahány élete, amik bizony akár el is
fogyhatnak:
interface Szereplo
{
attribute long oszlop;
attribute long sor;
};
interface Hos : Szereplo
{
attribute long megtalaltErtekek;
readonly attribute long eletek;
void megtalaltam(in Kincs kincs);
boolean megettek();
};
A CORBA objektumokat leíró IDL interfész definíciója az interface kulcsszóval kezdődik, amit a név, majd
nyitó és záró kapcsos zárójelek közé zárt példány- és módszerdeklarációk követnek. Az interfészek közötti
öröklődési kapcsolatot a kettőspont jelöli. Az attribute kulcsszóval kezdődik a példányok deklarációja. A
Page 103
A programozásról
75 Created by XMLmind XSL-FO Converter.
CORBA objektumnak majd minden példányához implementálnia kell egy lekérdező és egy beállító metódust,
illetve a readonly taghoz csak egy lekérdezőt. Az in szócska arra utal, hogy a kincs a CORBA objektum
bemenő adata.
Az imént említett példa egyébként része a Elosztott objektumok - Elosztott labirintus című ponthoz készített
CORBA objektumok IDL interfészeit tartalmazó elosztott.idl állománynak.
Az interfészek elkészítése után az említett idlj fordítóval elvégezzük a megfelelő Java nyelvi leképezést
C:\...> idlj -fall -falltie elosztott.idl
a parancsban felhasznált -fall -falltie kapcsolók arra utalnak, hogy mely kapcsolódó Java forrásokat, mely
CORBA objektum implementálási stratégia mentén kívánjuk legeneráltatni. A legenerált Java forrásokra -
esetünkben egy ilyen például a HosOperations.java állományra - támaszkodva implementáljuk a CORBA
objektum metódusait:
public class HősKiszolgáló
extends SzereplőKiszolgáló
implements HosOperations {
...
/**
* Jelzi, hogy éppen megettek.
*
* @return true ha a hősnek még van élete, ellenkező esetben,
* azaz ha az összes élete elfogyott már, akkor false.
*/
public boolean megettek() {
if(életekSzáma > 0) {
--életekSzáma;
return false;
} else
return true;
}
/**
* Gyüjtögeti a megtalált kincseket.
*
* @param kincs amit éppen magtalált a hős.
Page 104
A programozásról
76 Created by XMLmind XSL-FO Converter.
*/
public void megtalaltam(Kincs kincs) {
megtaláltÉrtékek += kincs.ertek();
}
és persze az IDL interfészben specifikált megtaláltErtekek taghoz készítünk egy lekérdező és egy beállító
módszert és az eletek taghoz egy lekérdezőt, mivel ez csak olvasható tagnak specifikáltuk. Ezek után jöhet a
klasszikus fordítás, majd a futtatás, de mindezzel a Elosztott objektumok - Elosztott labirintus című pontban
folytatjuk.
1.3. Az internet, a programozó tágabb hazája
„A gépek ilyenformán szakadatlanul együttműködő egységet alkotnak, s e nagy egységen
belül a nyilvántartott adatok és ellentmondások folytonos változása szükségszerűen megy
végbe.” —Wigner Jenő
Az internet megjelenéséig programjaink csak egy adott számítógépben éltek, léteztek. Napjainkban viszont már
olyan programokat is írhatunk, amik egy absztraktabb szinten sokkal tágasabb lakhelyet tudhatnak magukénak:
számos, a legkülönfélébb módon összekapcsolt mindenféle számítógép hálózatát.
Tekintsük például a SETI@Home projektet [SETI HOME], melyben az arecibói rádióteleszkóp vette adatokat
szétosztják az interneten keresztül jelentkező lelkes felhasználók között, akik gépidejükből szánnak a
SETI@Home projekttől kapott részadatok elemzésére. Olyan jeleket keresnek a rádióháttérben, amik idegen
civilizációk üzenetei lehetnek. Ezen a számításon a SETI@Home projekt sok millió felhasználója osztozik! (A
SETI témában általában a már eddig is többször hivatkozott [KAPCSOLAT REGÉNY] elolvasását javasoljuk
érzelmi megalapozásként az érdeklődő Olvasónak.)
A gépek hálózatának szoftveres alapja a 1969 végétől négy amerikai; az UCLA, UCSB, SRI és az utah-i
egyetemek között kiépülő és ébredő ARPANET hálózatra fejlesztett TCP/IP hálózati szoftver.
1.3.1. TCP/IP
„...míg az Internet szabványosítási konferencián a résztvevők farmert viselnek (kivéve a San
Diegóban megrendezett találkozókat, amikor rövidnadrágot és pólót).” —Andrew S. Tanenbaum
Page 105
A programozásról
77 Created by XMLmind XSL-FO Converter.
1. A fizikai réteg nem a programozók, inkább a mérnökök világa. Tipikusan mérnöki kérdések merülnek fel,
mert a réteg feladata a gépek valódi, fizikai összekapcsolása. Az e feletti rétegekre viszont már mint
szoftverekre gondoljunk.
2. A hálózati rétegben megjelenik az IP szám, amivel illetni tudunk egy gépet, pontosabban egy, a gépben lévő
hálókártyát.
3. A szállítási rétegben lehetővé válik egy IP számon belül disztingválni. Attól függően, hogy a „Megbízható
kommunikációt akarunk?” kérdésre a válasz igen, akkor 0-65535 számú TCP kapunk van. Ha a válasz nem,
akkor ugyanennyi UDP kapu.
4. Az alkalmazási rétegben találjuk az olyan ismert protokollokat, mint a HTTP - az internetes böngészés, vagy
az SMTP - az internetes levelezés protokollja. A továbbiakban mindkét említettel, demonstrációs céllal
részletesebben is foglalkozunk majd.
1.3.2. A kliens-szerver modell
A szerver az a program, aki valamilyen szolgáltatást nyújt, a kliensek pedig azok a programok, akik ezeket a
szerverek nyújtotta szolgáltatásokat igénybe veszik.
Nézzünk példákat sikeres szolgáltatásokra, azaz olyanokra, amik külön kaput (well-known port, jól ismert
portot) is kaptak szolgáltatásuk nyújtására. Ezek a kapuk az 1024 alatti tartományból kerülnek ki, a
http://www.iana.org/assignments/port-numbers címen, vagy Linuxunkon a /etc/services állományban lehet
csemegézni, hogy melyek is ezek.
[norbi@niobe ~]$ more /etc/services.
.
.
ftp 21/tcp
ftp 21/udp fsp fspd
ssh 22/tcp # SSH Remote Login Protocol
ssh 22/udp # SSH Remote Login Protocol
.
.
.
smtp 25/tcp mail
smtp 25/udp mail
.
.
.
http 80/tcp www www-http # WorldWideWeb HTTP
http 80/udp www www-http # HyperText Transfer Protocol
.
.
.
1.3.3. Bepillantás az alkalmazási rétegbe: a HTTP protokoll
Dolgozzunk egy kicsit az alkalmazási réteg szintjén, nézzük meg Linuxunkon működés közben a HTTP
protokollt! Fut a gépünkön a webszerver folyamat? Ezt egyszerű felhasználóként is megnézhetjük a webszerver
(http démon) SysV indítószkriptjének status kapcsolójával:
[norbi@niobe ~]$ /etc/rc.d/init.d/httpd status
A(z) httpd le van állítva
tehát nem megy. Futtassuk, mert vele tudunk majd TCP/IP-n keresztül HTTP párbeszédet folytatni.
Rendszergazdaként tudjuk lefuttatni a webszerver megfelelő elindítását elvégző szkriptet a start kapcsolóval
Page 106
A programozásról
78 Created by XMLmind XSL-FO Converter.
[root@niobe ~]# /etc/rc.d/init.d/httpd start
httpd indítása: [ OK ]
Megint csak egyszerű felhasználóként lépjünk kapcsolatba a gépünkön a 80-as TCP kapun a kliensek
kapcsolatfelvételeire váró webszerver folyamattal és kérjük el tőle a webterület gyökerének index.html
állományát:
[norbi@niobe ~]$ telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
a telnet programot, most mint TCP kliens programot használjuk arra, hogy TCP csatornán keresztül tudjunk
kommunikálni a webszerverrel. Össze kell állítanunk a HTTP kérést! Ennek első sora, hogy GET, azaz
szeretnénk elkérni a webterület gyökeréből az index.html állományt és mellesleg a HTTP/1.0 protokollt
beszéljük:
[norbi@niobe ~]$ telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
GET /index.html HTTP/1.0
Az első sort követhetik kulcs-érték párok, ha valamit szeretnénk közölni a szerverfolyamattal, de mivel most
semmit, jöhet a kérés törzse, ami most megint csak legyen üres, így gyakorlatilag két entert nyomunk és máris
jön a HTTP válasz a szervertől:
[norbi@niobe ~]$ telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
GET /index.html HTTP/1.0
HTTP/1.1 200 OK
Date: Sun, 10 Sep 2006 08:20:31 GMT
Server: Apache/2.2.0 (Fedora)
Last-Modified: Sun, 10 Sep 2006 08:17:16 GMT
ETag: "158540-6f-41d1511bd9b00"
Accept-Ranges: bytes
Content-Length: 111
Connection: close
Content-Type: text/html; charset=UTF-8
<html>
<head>
<title>Hello, Javat tanitok!</title>
</head>
<body>
Hello, Javat tanitok!
</body>
</html>Connection closed by foreign host.
Page 107
A programozásról
79 Created by XMLmind XSL-FO Converter.
A HTTP válasz első sora a protokoll, amit a szerver beszél, aztán a válasz kódja és rövid szöveges feloldása:
most OK, azaz OK! Ezt követi mindenféle kulcs-érték pár, majd a válasz testébe csomagolva a kért állomány. Ez
történik a háttérben, amikor böngészünk. Ha például egy honlapon egy pdf állományra kattintunk, akkor a
válaszban a küldött tartalom Content-Type: application/pdf típusa ez lenne, innen tudja majd a böngésző,
hogy nyitni kell a pdf megjelenítőt.
Persze ugyanezt Windows alatt is megnézhetjük egy Start/Kellékek/Parancssor parancsablakból, ha egy
webszervert futtató gép 80-as kapujára csatlakozunk:
C:\Documents and Settings\norbi>telnet www.inf.unideb.hu 80
Figyelmeztetés a Javaban kezdő Olvasóknak
A következő példa megint csak nem a teljesen kezdő Javasoknak szól, ők szokás szerint ugorják át és
majd például a Az első Java tapasztalatok című pont után térjenek ide vissza.
1.32. példa - Port szkennelő példa
Dolgozzunk egy kicsit az szállítási réteg szintjén! Írjunk egy a socket programozás absztrakciós szintjére
helyezhető példát!
Írjunk egy rövid példát, ami megnézi, hogy gépünk éppen milyen portokat figyel.
public class KapuSzkenner {
public static void main(String[] args) {
for(int i=0; i<1024; ++i)
try {
java.net.Socket socket = new java.net.Socket(args[0], i);
System.out.println(i + " figyeli");
socket.close();
} catch (Exception e) {
System.out.println(i + " nem figyeli");
}
}
}
Page 108
A programozásról
80 Created by XMLmind XSL-FO Converter.
A program végigzongorázza a parancssorában megkapott nevű gép 1024 alatti számú TCP kapuit: megpróbál
egy TCP kapcsolatot
java.net.Socket socket = new java.net.Socket(args[0], i);
létrehozni, ha sikerül, akkor a célporton ül egy szerver folyamat, ha nem, azaz ha kivétel keletkezik, akkor nem.
Egyébként siker esetén sem csinálunk semmit, hanem csak bezárjuk az éppen elkészített kliensoldali
kommunikációs végpontot reprezentáló socket objektumot.
[norbi@omega ~]$ java KapuSzkenner niobe
.
.
.
19 nem figyeli
20 nem figyeli
21 figyeli
22 figyeli
23 nem figyeli
24 nem figyeli
25 figyeli
26 nem figyeli
.
.
.
79 nem figyeli
80 figyeli
81 nem figyeli
82 nem figyeli
.
.
.
Csak saját gépre
Fontos, hogy ezt a kapufigyelő programunkat ne szabadítsuk rá az internetre, azaz ne engedjük rá
tetszőleges gépre, hanem csak az otthonira. Mert egy túlérzékeny rendszergazda még (jogosan) úgy
érezné, hogy támadás fenyegeti a gépét. Ha ilyen gépen akarjuk kipróbálni, például az iskolában, azt
előtte beszéljük meg a megfelelő rendszergazdával.
1.33. példa - Jól ismert portok feladat
Milyen szolgáltatások futnak a fent leportszkennelt gépen?
Page 109
81 Created by XMLmind XSL-FO Converter.
2. fejezet - Saját világok teremtése és Java alapok
„Döcögő egyetértés és futó programok” — David Clark [HÁLÓZATOK KÖNYV] [INTERNET REGULATION]
„A tigrist előbb gondolatban el kell ejteni - a többi formalitás” — Konfuciusz
Az OO programozó mentális világokat teremt, mint ahogyan mi is ezt fogjuk tenni ebben a fejezetben.
Kifejlesztünk egy labirintus API interfészt, azaz megteremtjük a saját labirintus játékunk képzeletbeli világát. S
e fejlesztési munka során ismerkedünk meg a Java OO programozás alapjaival. Itteni munkánk gyümölcse, a
kifejlesztett labirintus API szolgál majd a következő fejezetek fejlesztendő esettanulmányainak alapjául.
Ebben a gyakorlati programozási részben
• elkezdünk egy saját labirintus OO világot teremteni
• miközben megismerjük az OO és konkrétan a Java OO programozás alapfogalmait
1. A Java világa
„Fel hát csatázni, fel hát lelkesülni
Az új tanért. Alkotni új világot,”
—Madách Imre Az ember tragédiája
Lépjünk hát be a Java világába! Első lépésünk a Java SE, ME fejlesztői csomag telepítése, beállítása legyen. Itt
általános szabály, hogy mindig a legfrissebb verziójú Java SE, ME fejlesztői csomagokat használjuk. A Java
telepítése gépünkre pontban részletes segítséget adunk a letöltésről, telepítésről és az esetleges további
beállításokról mind a Windows, mind a Linux operációs rendszereket futtató Olvasóknak egyaránt.
A továbbiakban tehát feltesszük, hogy a megfelelő Javát az előző bekezdés hivatkozta pont értelmében
beállítottuk és egyik kezünk mindig a billentyűzeten...
1.1. A Java nyelv
Minden programhoz kapcsolható egy sajátos világ, egy mikrokozmosz.
A kisebb, egyszerűbb programok esetén ez a mikrokozmosz általában nem külön létező, hanem csupán egy már
létező világ parányi, kiragadott része. Erre példaként tekintsük azt a szokásos C programot, ami a sztenderd
inputját másolja a sztenderd outputjára és a bemenet vége jelre kilép.
Figyelmeztetés a C-ben járatlan Olvasóknak
Az alábbi széles körben elterjedt, szokásos C bevezető példa a hasonló szerkezetű és működésű Java
példát vezeti be, rövid átfutása után akár át is ugorható.
#include <stdio.h>
int
main (void)
{
int c;
while ((c = getchar ()) != EOF)
putchar (c);
Page 110
Saját világok teremtése és Java
alapok
82 Created by XMLmind XSL-FO Converter.
return 0;
}
Az Olvasó az alábbi módon fordíthatná és futtathatná ezt a masol.c forrásállományba illesztett másoló kódot.
[norbi@niobe ~]$ gcc masol.c -o masol
[norbi@niobe ~]$ ./masol
q
q
w
w
e
e
r
r
t
t
A gcc fordítóval lefordítottuk a masol.c forrást és a fordítás eredményéül kapott bináris futtatható állományt a
masol nevű állományba tetettük a fordító -o kapcsolójának segítségével. Majd az aktuális könyvtárból, azaz a
./állománynév alakban futtattuk a masol nevű állományt. A futás alatt a billentyűzetről beütött és enterrel
kísért q, w, e, r, t karaktereket a program a kimenetén, azaz a képernyőn visszhangozta, egészen a bemenet
vége, azaz a Ctrl+d billentyűkombináció megnyomásáig.
Ennek a C programnak a világát az stdin - most a billentyűzet, az stdout - most a képernyő, a UNIX világból
ismerős elemei, továbbá az egész számok, köztük az EOF jelnek megfelelő szám alkotják. Ebben a világban a
program szerint az történik, hogy számot olvasunk a program stdin bemenetről a getchar() függvénnyel és
írjuk a program stdout kimenetre, s mindaddig folytatjuk ezt, amíg az EOF számot nem olvassuk a bemeneten.
Hogyan fest az iménti C kódnak megfelelő Java program mikrokozmosza?
public class Másol {
public static void main(String[] args) throws Exception {
int i = 0;
while((i=System.in.read()) != -1)
System.out.printf("%c", i);
}
}
Az Olvasó az alábbi módon fordíthatná és futtathatná ezt a Másol.java forrásállományba illesztett másoló
osztályt.
[norbi@niobe ~]$ javac Másol.java
[norbi@niobe ~]$ java Másol
q
q
w
w
e
e
r
r
t
t
Page 111
Saját világok teremtése és Java
alapok
83 Created by XMLmind XSL-FO Converter.
A javac fordítóval lefordítottuk a Másol.java forrást, a fordítás eredményéül kapott bináris bájtkód állományt
a Másol.class nevű állományba kaptuk. Amit a java Másol parancs kiadásával - a .class kiterjesztést soha
nem, mindig csupán az osztálynevet kiírva - adtunk át bemenetként a Java Virtuális Gépnek. A futás alatt
ugyancsak a billentyűzetről beütött és enterrel kísért q, w, e, r, t karaktereket a program a kimenetén, azaz a
képernyőn visszhangozta, egészen a bemenet vége, azaz a Ctrl+d billentyűkombináció megnyomásáig.
Ennek a Java programnak a világát csatornák, a System.in sztenderd bejövő és a System.out sztenderd
kimenő csatornák alkotják. Egészen pontosan a System osztály InputStream típusú in és PrintStream típusú,
azaz PrintStream osztálybeli out változója vagy tagja. Ezen felül a működés pedig megegyezik az imént
tárgyalttal annyi kiegészítéssel, hogy a System.in bejövő csatorna read() függvénye a -1 érték visszaadásával
jelzi a csatorna végét.
A nagyobb, bonyolultabb programok esetén maga a program, azaz a programozó építheti fel a mikrokozmoszt.
Példaként tekintsünk egy labirintus játék programot, amiben a világ maga a labirintus, kincsestől, szörnyestől,
hősöstől és a szokásos történik: a hős bolyong, a szörnyek próbálják megenni, a kincs várja, hogy rábukkanjanak
stb.
Az OO programozás ereje abban rejlik, hogy a programozó a fejlesztés során arra van utalva, hogy saját
világokat építsen, teremtsen! Építőelemei a választott OO platform API interfészének osztályai, habarcsa pedig
maga a választott OO nyelv.
A következőkben a Java nyelvvel egy labirintus játék világának felépítése során ismerkedünk meg.
1.1.1.
1.1.1.1.
1.1.1.1.1. Saját labirintus feladat
2.1. példa - Saját labirintusunk elképzelése
Mindenki képzelje el saját labirintusát! Egy négyzethálós lapon készítsünk vázlatos rajzot a szerkezetéről,
foglaljuk össze írásban, mik vannak egy labirintusban!
Mi a labirintusunkat a következőképpen képzeljük el.
Ez egy 10 cella széles és 10 cella magas labirintus. A falat tégla színnel jelöltük, a járatot világossárgával.
Persze olyan is lehetne, hogy jóval nagyobb labirintust készítenék és azt, hogy egy cella éppen járat vagy fal
véletlenül döntenénk el: minden egyes ilyen döntésnél fej vagy írás érmét használnánk, de legalábbis egy ennek
megfelelő véletlenszám generátort, azaz egy olyan programot (vagy éppen csak egy java.util.Random
osztálybeli objektumot) ami a fej vagy írás dobálást szimulálja.
Aztán legyenek a labirintusban kincsek, szörnyek és maga a hős. Sok értékes kincs, kevés szörny és egy hős. A
játék, hogy a hőssel minél gyorsabban össze kell gyűjteni a kincseket, miközben pedig el kell kerülni a
szörnyeket. A kincsek és a szörnyek minden játékban máshonnan induljanak!
Page 112
Saját világok teremtése és Java
alapok
84 Created by XMLmind XSL-FO Converter.
1.1.2. Osztályok és objektumok
Milyenek is a labirintusok, milyen is a mi, az előző pontban elképzelt labirintusunk? Gyűjtsük most össze a
lényeges jellemzőit, tulajdonságait, részeit! Majd ezt követően Java nyelven írjuk le!
Mindenekelőtt a labirintusnak van mérete: szélessége és hosszúsága. Ez két szám, két egész szám. Javaban az
egész értékeket így írjuk le:
int szélesség;
int magasság;
és úgy olvassuk, hogy a szélesség és a magasság int típusú változók. Ezeknek a változóknak az értéke az előző
labirintus példánál maradva 10 és 10.
A labirintus lényegéhez tartozik a szerkezete, azaz, hogy hol van fala. Mert ahol nincs, ott ugye járata van. Ezt a
labirintus elképzelése során, a négyzethálós lapon egy kétdimenziós tömb alakjában rajzoltuk le. Javaban ha
nem egyetlen számot akarunk reprezentálni, hanem számok ilyen szabályos elrendezését, tömbjét, akkor ezt
írjuk:
int[][] szerkezet;
ahol a [5][6] majd például azt mondja meg, hogy az 5. sor 6. oszlopában fal van vagy járat, azaz az itt lévő
szám 0 vagy 1, most éppen járat, azaz 0. Az ábrán egyben azt is észrevételezhetjük, hogy a Java tömbök
elemeinek indexelése, azaz sorszámozása nullától indul.
Page 113
Saját világok teremtése és Java
alapok
85 Created by XMLmind XSL-FO Converter.
Mivel labirintus terünk olyan, hogy itt csak ez a két érték jöhet szóba, az int típus használata jelen esetben
pazarló, mert az int értéke nem csupán 0 vagy 1 lehet, hanem a -231 számtól a 231-1 számig bármi, ami
búzaszemben nem olyan sok, mintha egy szemből indulva a sakktábla minden cellájában megdupláznánk a
szemeket, de azért jó nagy.
Használjunk helyette boolean típust, mert ennek csupán két értéke lehet: a hamis - Javaban false - vagy az igaz
- true. Esetünkben a tervben szereplő 0 értéket képzeljük a false boolean értéknek, az 1 értéket true boolean
értéknek. Ekkor a
boolean[][] szerkezet;
tömb [5][6] eleme majd arra a kérdésre válaszol, hogy fal van-e ezen a helyen a labirintusunkban?
A válasz hamis, azaz nem fal van ott, hanem járat.
Foglaljuk most össze, hogyan írtuk le labirintusunkat:
int szélesség;
int magasság;
boolean[][] szerkezet;
Java nyelven a fejlesztendő programunk világabeli dolgok tulajdonságait osztályokba foglalva írjuk le. Az
osztálynak van neve, ez utaljon magára arra a dologra, amit az osztály le akar írni és megegyezés szerint
kezdődjék nagy első betűvel. Ha több szóból áll, akkor minden alkotó rész szó kezdődjék nagy első betűvel. A
mi labirintus osztályunkat nevezzük egyszerűen Labirintus osztálynak és tartalmazza tehát azokat az általános
jellemzőket, amit a labirintusról éppen most összeszedtünk. A név elé írjuk a class kulcsszót, majd az osztály
nevét, végül az osztály tulajdonságait foglaljuk nyitó és záró kapcsos zárójelek közé.
class Labirintus {
int szélesség;
int magasság;
boolean[][] szerkezet;
}
Megvan általában a labirintusunk leírása, de nekünk egy konkrét játékban egy konkrét labirintusra lesz
szükségünk, azaz egy a Labirintus osztályból származó konkrét labirintusra, egy labirintus objektumra. Azt a
folyamatot, amikor egy osztályból konkrét példányt, egy objektum példányt hozunk létre, példányosításnak
Page 114
Saját világok teremtése és Java
alapok
86 Created by XMLmind XSL-FO Converter.
nevezzük. A példányosítást egy speciális függvény, a konstruktor végzi. De mielőtt tovább folytatnánk,
elöljáróban következzék még néhány gondolat. A Java nyelv, bármennyire is csodálatos nyelv, de mégiscsak
egy nyelv. A következő pontban - egy rövid kitérő erejéig - ennek megfelelően közelítünk majd a Java
programozási nyelvhez. Ebben a tárgyalásban a beszélt nyelvek - mint például a magyar nyelv - felől közelítünk
a pogramozási nyelvhez, mint ahogyan hasonlóan ezt a megközelítést követtük - az egyébként gyermekeknek
szánt - [FANTASZTIKUS PROGRAMOZÁS] könyvben is. Csak érdekességként jegyezzük meg, hogy a
fordított folyamat, amely során a programozási nyelvek világa felől közelítünk a beszélt nyelvekhez, Noam
Chomsky nyelvész munkássága nyomán [CHOMSKY NYELVOSZTÁLYOK] ma az egyik legalapvetőbb
informatikai és fontos nyelvészeti diszciplina.
1.1.3. A programozás világnyelve a Java
A Java nyelv nem beszélt, hanem írott nyelv, amin a programozók „beszélnek”, azaz írnak. A Java nyelvnek
napjainkra három nagy nyelvjárása alakult ki, ezek a Java ME, a Java SE és a Java EE. Az ME (a Micro Edition
- mikro kiadás) a mobiltelefonokat programozók közösségében terjedt el, az EE (az Enterprise Edition - vállalati
kiadás) az ipari szférában hódít, az SE (a Standard Edition - általános kiadás) pedig az a közös gyökér, amiből
ezek a dialektusok mára kifejlődtek. De ne becsüljük le a Java SE-t, mert a Java SE API hatalmas. Csak egy
példával érzékeltetve: a Java nyelven implementáló CORBA programozó is a Java SE nyelvjárást beszéli! Ami
ezeket a nyelvjárásokat elidegeníthetetlenül összeköti, az maga a Java nyelv, a Java nyelv nyelvtana.
1.1.3.1. A Java nyelvtana
A Java nyelvben használt betűk lehetnek akár ékezetesek is, mert a Java forrásállományokban tetszőleges
Unicode karaktereket felhasználhatunk. Az egymás után írt betűket szavaknak nevezzük.
1.1.3.1.1. A Java szófajok
A Java szavak háromfélék lehetnek, ezek a
• kulcsszavak
• az azonosító szavak
• és a kifejezés szavak.
A Java szófajok legegyszerűbbike a kulcsszó, mert a Java kulcsszavak véges kevesen vannak. Könnyen
megadhatjuk őket egy egyszerű felsorolással. Ezt a felsorolást azzal segítjük, hogy a kulcsszavakat további
három alszófajba osztjuk, ezek a
• melléknevek
• a típus nevek
• és a vezérlő nevek
Figyelmeztetés a Javaban kezdő Olvasóknak
A következőkben valódi Java forráskódrészletek következnek. Ne értelmezni próbáljuk ezeket, hanem
csupán az említett szavak előfordulásait figyeljük meg bennük!
A Java melléknevek a következők:
• static jelentése, hogy nem példányhoz, hanem osztályhoz tartozó. Például a Labirintus osztálybeli
/** Normál működés, a hőssel időközben semmi nem történt. */
public static final int JÁTÉK_MEGY_HŐS_RENDBEN = 0;
sorok azt jelentik, hogy a JÁTÉK_MEGY_HŐS_RENDBEN egész típusú változó statikus, azaz nem a Labirintus
osztálybeli példányokhoz, hanem magához a Labirintus osztályhoz tartozik. Nem minden labirintus
Page 115
Saját világok teremtése és Java
alapok
87 Created by XMLmind XSL-FO Converter.
objektumnak van meg ez az egész típusú változója, hanem az esetlegesen létrehozott több labirintus
objektumnak van egy közös JÁTÉK_MEGY_HŐS_RENDBEN nevű változójuk. Tehát - akár a Labirintus
osztályból való példány létrehozása nélkül - az osztálynévvel minősítve, a
Labirintus.JÁTÉK_MEGY_HŐS_RENDBEN formában használhatjuk ezt a változót. Így teszünk például a
LabirintusJáték osztályban:
switch(labirintus.bolyong(hős)) {
case Labirintus.JÁTÉK_MEGY_HŐS_RENDBEN:
break;
ahol a program szövegrészlet azt mondja, hogy a hős labirintusbeli bolyongása során nem csinálunk semmit,
ha a hőssel nem történik semmi.
• final jelentése, hogy nem módosítható. Például az előző Labirintus osztálybeli példánál maradva a
/** Normál működés, a hőssel időközben semmi nem történt. */
public static final int JÁTÉK_MEGY_HŐS_RENDBEN = 0;
sorokban a final melléknév azt jelenti, hogy a JÁTÉK_MEGY_HŐS_RENDBEN nem módosítható a futás során.
Ez azt jelenti, hogy a JÁTÉK_MEGY_HŐS_RENDBEN = 42; értékadást tartalmazó programot a fordító le sem
fordítja, hanem hibát jelez, mert a változót nem módosíthatónak mondtuk!
C:\...\Munkakönyvtár>javac javattanitok\labirintus\Labirintus.java
javattanitok\labirintus\Labirintus.java:44: cannot assign a value
to final variable JÁTÉK_MEGY_HŐS_RENDBEN
JÁTÉK_MEGY_HŐS_RENDBEN = 42;
^
1 error
• public jelentése, hogy bárhonnan látható. Próbáljuk ki, hogy az imént, a Labirintus osztályból idézett
sorokból kihagyjuk a public melléknevet:
/** Normál működés, a hőssel időközben semmi nem történt. */
static final int JÁTÉK_MEGY_HŐS_RENDBEN = 0;
Az így módosított javatanitok.labirintus csomagbeli labirintus API-val már nem fog lefordulni például
a javatanitok csomagbeli, a javatanitok.labirintus.Labirintus osztályt használó
LabirintusVilág program, mert nem látja az immár nem publikus tulajdonságú JÁTÉK_MEGY_HŐS_RENDBEN
tagot:
C:\...\Munkakönyvtár>javac javattanitok\LabirintusVilág.java
javattanitok\LabirintusVilág.java:83: JÁTÉK_MEGY_HŐS_RENDBEN is not public
in javattanitok.labirintus.Labirintus; cannot be accessed from outside package
case Labirintus.JÁTÉK_MEGY_HŐS_RENDBEN:
^
1 error
Page 116
Saját világok teremtése és Java
alapok
88 Created by XMLmind XSL-FO Converter.
• private jelentése, hogy csak a saját osztályában látszik. Például az előző pont módosításához hasonlóan
végezzük el az alábbi átalakítást:
/** Normál működés, a hőssel időközben semmi nem történt. */
private static final int JÁTÉK_MEGY_HŐS_RENDBEN = 0;
ennek megfelelően fordításkor az alábbi hibát kapjuk:
C:\...\Munkakönyvtár>javac javattanitok\LabirintusVilág.java
javattanitok\LabirintusVilág.java:83: JÁTÉK_MEGY_HŐS_RENDBEN has private access
in javattanitok.labirintus.Labirintus
case Labirintus.JÁTÉK_MEGY_HŐS_RENDBEN:
^
1 error
ha ragaszkodnánk a privát tulajdonsághoz, akkor ezt a hibát csak úgy háríthatnánk el, ha csakis a
javatanitok.labirintus.Labirintus osztályban használnánk a továbbiakban a
JÁTÉK_MEGY_HŐS_RENDBEN tagot.
• protected jelentése, hogy csak a saját és a leszármazott osztályokban látszik. Például az előző pontok
módosításaihoz hasonlóan végezzük el az most az alábbi módosítást:
/** Normál működés, a hőssel időközben semmi nem történt. */
protected static final int JÁTÉK_MEGY_HŐS_RENDBEN = 0;
aminek megfelelően most fordításkor a
C:\...\Munkakönyvtár>javac javattanitok\LabirintusVilág.java
javattanitok\LabirintusVilág.java:83: JÁTÉK_MEGY_HŐS_RENDBEN has protected access
in javattanitok.labirintus.Labirintus
case Labirintus.JÁTÉK_MEGY_HŐS_RENDBEN:
^
1 error
hibát kapjuk. Ha ragaszkodnánk a védett tulajdonsághoz, akkor ezt a hibát csak úgy háríthatnánk el, ha csakis
a javatanitok.labirintus.Labirintus osztályban vagy annak leszármazottaiban használnánk a
továbbiakban a JÁTÉK_MEGY_HŐS_RENDBEN tagot.
Megjegyezhetjük, hogy a public, static, final jellemzésű változókat konstansoknak nevezzük. A JDK
könyvtárában található src.zip állományban - ahol megtaláljuk a Java SE OO világ összes osztályának
forráskódját - a src.zip/java/lang/Math.java forrásban leírt Math osztályban találjuk például a Pi
konstans definícióját: public static final double PI = 3.14159265358979323846;
• void jelentése, hogy nem ad vissza értéket. Például a LabirintusJáték osztályban a
/**
* Ébresztő az várakozó rajzolást végző szálnak, ki kell rajzolni a játék
* grafikus felületét.
*/
synchronized public void rajzolniKell() {
Page 117
Saját világok teremtése és Java
alapok
89 Created by XMLmind XSL-FO Converter.
notify();
}
sorok azt mondják, hogy a rajzolniKell() függvény nem ad vissza semmit, mert hiszen nem is számol ki
semmit. Feladata csupán annyi, hogy az éppen egy wait() hívásban alvó végrehajtási szálat a notify();
függvény meghívásával felébressze.
• synchronized jelentése, hogy korlátozza, szabályozza a szálak futását. Az imént látott notify() függvény a
java.lang.Object osztály - minden Java osztály ősének - metódusa, ami egyben azt is jelenti, hogy ezzel a
metódussal minden Java osztály rendelkezik. Mint ahogyan a a függvény párjával, a wait() metódussal is.
Ez utóbbi függvény hívása elaltatja a hívást végrehajtó programszálunk futását, az előbbi pedig az ilyen alvó
szálakat ébreszti fel. Szabály, hogy egy objektum notify() függvényét szinkronizáltan hívjuk. Szinkronizált
a hívás, ha az objektum egy szinkronizált példány vagy az objektum osztályának szinkronizált osztály
metódusából, vagy az objektum egy szinkronizált programszöveg blokkjából történik. Ezt a szinkronizált
tulajdonságot jelöli a synchronized melléknév. A fenti példában a synchronized public void
rajzolniKell() { szinkronizált, példányhoz tartozó rajzolniKell() metódust láttuk.
A Java típusnevek a következők: int, long, byte, char, float, double, boolean. A típusnevek használatával a
Típusok és változók című pontban foglalkozunk részletesen.
A Java vezérlő nevek a következők:
• import jelentése, más osztályok használatának jelzése. Például a LabirintusVilág osztálybeli
import javattanitok.labirintus.*;
sor azt mondja, hogy az osztály használja a javattanitok.labirintus csomagot, a kézikönyvhöz
fejlesztett labirintus API-t. Ha ezt a sort kitörölnénk az osztály forráskódjából, akkor a
C:\...\Munkakönyvtár>javac javattanitok\LabirintusVilág.java
javattanitok\LabirintusVilág.java:31: cannot find symbol
symbol : class Labirintus
location: class javattanitok.LabirintusVilág
protected Labirintus labirintus;
^
javattanitok\LabirintusVilág.java:33: cannot find symbol
symbol : class Hős
location: class javattanitok.LabirintusVilág
protected Hős hős;
^
javattanitok\LabirintusVilág.java:47: cannot find symbol
symbol : class RosszLabirintusKivétel
location: class javattanitok.LabirintusVilág
throws RosszLabirintusKivétel {
...
11 errors
hibákat adná a fordító. Mivel nem jeleztük, hogy használjuk a javattanitok.labirintus csomagot, így a
fordító nem találja az ebben a csomagban elhelyezett Labirintus, Hős, RosszLabirintusKivétel stb.
használt osztályokat.
Az import utasításban a csillag a csomag összes osztályát jelenti. A LabirintusKiszolgáló osztálybeli
Page 118
Saját világok teremtése és Java
alapok
90 Created by XMLmind XSL-FO Converter.
import java.util.List;
import java.util.Iterator;
sorok azt mondják, hogy a java.util csomagnak csupán a List és az Iterator osztályát használja a
LabirintusKiszolgáló osztály.
• package jelentése, hogy egy adott csomagba tartozó rész következik. Például a Labirintus osztály
Labirintus.java forrásának első
package javattanitok.labirintus;
utasátása azt mondja, hogy a Labirintus.java állomány tartalma a javattanitok.labirintus csomagba
fog kerülni. Fontos szabály, hogy ennek megfelelően a Labirintus.java állományt egy, a csomag nevének
megfelelő javattanitok\labirintus könyvtárban kell elhelyezni.
Ha a Labirintus.java forrásból a package javattanitok.labirintus; sort kitörölnénk, a következő
fordítási hibát kapnánk:
C:\...\Munkakönyvtár>javac javattanitok\LabirintusVilág.java
javattanitok\LabirintusVilág.java:31: cannot access javattanitok.labirintus.Labirintus
bad class file: .\javattanitok\labirintus\Labirintus.java
file does not contain class javattanitok.labirintus.Labirintus
Please remove or make sure it appears in the correct subdirectory of the classpath.
protected Labirintus labirintus;
mert a szóban forgó sor kivétele után a Labirintus osztály már nem tartja magát a
javattanitok.labirintus csomagba tartozónak.
• class jelentése, hogy osztály megadása következik. Például a Szereplő osztályt leíró Szereplő.java
állomány így kezdődik:
/*
* Szereplő.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok.labirintus;
/**
* A labirintus szereplőit (kincsek, szörnyek, hős) absztraháló osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.labirintus.Labirintus
*/
public class Szereplő {
• extends jelentése, hogy egy osztály kiterjesztése következik. A Hős osztályunk kiterjeszti a Szereplő
osztályt, ennek megfelelően a Hős forrása így kezdődik:
Page 119
Saját világok teremtése és Java
alapok
91 Created by XMLmind XSL-FO Converter.
/*
* Hős.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok.labirintus;
/**
* A labirintus hősét leíró osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.labirintus.Labirintus
*/
public class Hős extends Szereplő {
• new jelentése, hogy egy új objektumot készít. A LabirintusVilág osztályban például így készül el a
labirintus:
// A labirintus elkészítése állományból
labirintus = new Labirintus(labirintusFájlNév);
A new használatát példányosításnak nevezzük. Itt a Labirintus osztály olyan konstruktor függvényét hívtuk,
amely egy állományból, a labirintusFájlNév nevű állományból építi fel a labirintust.
• try catch kijelöli a megfigyelt forrásszövegrész blokkot és a kivételkezelő blokkot. Például a
LabirintusVilág osztály szokásosan indul, a statikus main() függvényében készít önmagából egy
objektum példányt:
try {
new LabirintusVilág(args[0]);
} catch(RosszLabirintusKivétel rosszLabirintusKivétel) {
a try blokkban figyeli, hogy nem történt-e valami probléma (kivétel) a példányosítás során, ha igen -
mondjuk nincs meg az args[0] megnevezte, a labirintus tervét tartalmazó állomány - akkor a program futása
a catch kivételkezelő RosszLabirintusKivétel ágán folytatódik.
• this jelentése: hivatkozás egy osztály aktuális példányára. A this használatára triviális példa a példánytagok
beállítása
public Labirintus(int szélesség, int magasság,
int kincsekSzáma, int szörnyekSzáma) {
this.magasság = magasság;
this.szélesség = szélesség;
...
Kevésbé egyszerű példa a MandelbrotHalmazNagyító osztályunkban szereplő
Page 120
Saját világok teremtése és Java
alapok
92 Created by XMLmind XSL-FO Converter.
public void mouseReleased(java.awt.event.MouseEvent m) {
if(m.getButton() == java.awt.event.MouseEvent.BUTTON1 ) {
double dx = (MandelbrotHalmazNagyító.this.b
- MandelbrotHalmazNagyító.this.a)
/MandelbrotHalmazNagyító.this.szélesség;
mert itt egy egéreseményekre reagáló addMouseListener(new java.awt.event.MouseAdapter() {
névtelen objektumon belül vagyunk, így a this önmagában ezt az eseménykezelő objektumot jelentené, ezért
minősítjük a MandelbrotHalmazNagyító osztálynévvel, mert nekünk az aktuális
MandelbrotHalmazNagyító objektumra van éppen szükségünk, hogy hozzáférjünk annak a, b és szélesség
tagjaihoz.
Részletesebben olvashat a this kulcsszóról a Vissza az OO-hoz [105] című részben.
• super hivatkozik egy osztály szülőjének aktuális példányára. Példáink közül a Pontmátrix osztályban
találkozunk a használatával:
/** A pontmátrixot tartalmazó kép kirajzolása. */
public void paintComponent(java.awt.Graphics g) {
super.paintComponent(g);
if(pontmátrixKép != null)
g.drawImage(pontmátrixKép, 0, 0, this);
}
• implements jelzi, hogy egy osztály megvalósítja egy interfész módszereit.
class Kiszolgáló implements Runnable {
A fenti, a Kiszolgáló osztályból származó osztálydefiníció feje mutatja, hogy a Kiszolgáló osztály
megvalósítja a Runnable - egyébként egyetlen - run() metódusát. Ennek megfelelően a Kiszolgáló
osztálynak rendelkeznie kell a run() függvény implementációjával, mint ahogyan rendelkezik is:
public void run() {
try {
java.io.BufferedReader bejövőCsatorna =
new java.io.BufferedReader(
new java.io.InputStreamReader(socket.getInputStream()));
java.io.PrintWriter kimenőCsatorna =
new java.io.PrintWriter(socket.getOutputStream());
String sor = null;
while((sor = bejövőCsatorna.readLine()) != null) {
kimenőCsatorna.println(sor);
kimenőCsatorna.flush();
}
socket.close();
} catch(java.io.IOException ioE) {
ioE.printStackTrace();
}
}
Page 121
Saját világok teremtése és Java
alapok
93 Created by XMLmind XSL-FO Converter.
• return jelentése: visszatérés függvényből.
public class Kincs extends Szereplő {
/** A kincs értéke. */
protected int érték;
...
/**
* Megmondja, hogy megtalálták-e már a kincset?
*
* @return true ha a kincset már megtalálták,
* ha még nem akkor false.
*/
public boolean megtalálva() {
return megtalálva;
}
A fenti, a Kincs osztályból származó kódcsipet jól mutatja, hogy a return utasítás nemcsak kiugrik a hívott
függvény végrehajtásából, hanem - és ez a tipikus használata - visszaadja a függvény által kiszámolt,
megfelelő típusú értéket.
• for jelentése az előírt lépésszámú ciklus bevezetése. A következő, a LabirintusKiszolgáló osztályból
származó példa végiglépked a kincsek lista kincsein.
for(Kincs kincs : kincsek) {
if(kincs.megtalalt(hős))
hős.megtalaltam(kincs);
}
Klasszikusabb használatot mutat a Labirintus osztályból kicsippentett, alábbi kódtöredék:
// Szörnyek létrehozása
szörnyek = new Szörny[szörnyekSzáma];
for(int i=0; i<szörnyek.length; ++i)
szörnyek[i] = new Szörny(this);
ami a szörnyek tömbön lépked végig.
• while jelentése, az elől tesztelő ciklus bevezetése. Az alábbi, a TCAG2Hexa osztálybeli ciklus
int i = 0;
while((i=System.in.read()) != -1) {
switch(i) {
case 'T':
második = 0;
break;
case 'C':
Page 122
Saját világok teremtése és Java
alapok
94 Created by XMLmind XSL-FO Converter.
második = 1;
break;
case 'A':
második = 2;
break;
case 'G':
második = 3;
break;
}
addig olvas a System.in bemenetről, tipikusan a billentyűzetről, ameddig csak lehet, tehát amíg a while után
szereplő feltétel kiértékelése igazat ad.
• do while jelentése a hátul tesztelő ciklus bevezetése. A Szereplő osztálybeli alábbi ciklus
// Többször próbálkozunk elhelyezni a szereplőt a labirintusban,
// számolja, hol tartunk ezekkel a próbálkozásokkal:
int számláló = 0;
do {
// itt +2,-2-k, hogy a bal alsó saroktól távol tartsuk
// a szereplőket, mert majd ezt akarjuk a hős kezdő pozíciójának
oszlop = 2+véletlenGenerátor.nextInt(maxSzélesség-2);
sor = véletlenGenerátor.nextInt(maxMagasság-2);
// max. 10-szer próbálkozunk, de ha sikerül nem "falba tenni" a
// szereplőt, akkor máris kilépünk:
} while(++számláló<10 && labirintus.fal(oszlop, sor));
addig próbálja elhelyezni a szereplőt a labirintusban, amíg nem falba teszi, de maximum 10 alkalommal
próbálkozik. Tehát addig megy vissza a do-ra, amíg a while után szereplő feltétel kiértékelése igazat ad.
• if else jelentése egy villa elágazás a végrehajtásban.
// Sejt cella kirajzolása
if(rács[i][j] == ÉLŐ)
g.setColor(java.awt.Color.BLACK);
else
g.setColor(java.awt.Color.WHITE);
g.fillRect(j*cellaSzélesség, i*cellaMagasság,
cellaSzélesség, cellaMagasság);
azaz vagy feketével vagy fehérrel töltjük ki a j*cellaSzélesség, i*cellaMagasság, cellaSzélesség,
cellaMagasság (bal felső sarok oszlop, sor és szélesség, magasság) adatokkal jellemzett téglalapot.
• if else if else jelentése egy többirányú elágaztatás a végrehajtásban.
// A billentyűzetről érkező események feldolgozása
addKeyListener(new java.awt.event.KeyAdapter() {
// Az 'k', 'n', 'l', 'g' és 's' gombok lenyomását figyeljük
public void keyPressed(java.awt.event.KeyEvent e) {
if(e.getKeyCode() == java.awt.event.KeyEvent.VK_K) {
// Felezük a cella méreteit:
cellaSzélesség /= 2;
cellaMagasság /= 2;
setSize(Sejtautomata.this.szélesség*cellaSzélesség,
Sejtautomata.this.magasság*cellaMagasság);
validate();
} else if(e.getKeyCode() == java.awt.event.KeyEvent.VK_N) {
Page 123
Saját világok teremtése és Java
alapok
95 Created by XMLmind XSL-FO Converter.
// Duplázzuk a cella méreteit:
cellaSzélesség *= 2;
cellaMagasság *= 2;
setSize(Sejtautomata.this.szélesség*cellaSzélesség,
Sejtautomata.this.magasság*cellaMagasság);
validate();
} else if(e.getKeyCode() == java.awt.event.KeyEvent.VK_S)
pillanatfelvétel = !pillanatfelvétel;
else if(e.getKeyCode() == java.awt.event.KeyEvent.VK_G)
várakozás /= 2;
else if(e.getKeyCode() == java.awt.event.KeyEvent.VK_L)
várakozás *= 2;
repaint();
}
});
a kódrészlet megállapítja, hogy a program által figyelt gombokat nyomtuk-e le és igenlő esetben végrehajtja a
megfelelő feladatot. Ha nem a figyelt gombok valamelyikét nyomtuk volna, akkor az utolsó if nélküli else
teljesülne, de ezt most elhagytuk.
• switch case jelentése több lehetőség közüli választás a végrehajtásban. A korábban bemutatott kódrészletbeli
switch utasítás
switch(i) {
case 'T':
második = 0;
break;
case 'C':
második = 1;
break;
case 'A':
második = 2;
break;
case 'G':
második = 3;
break;
}
eldönti, hogy az i a 'T', 'C', 'A', 'G' betűk közül esetlegesen melyik éppen az i.
• break jelentése kiugrás a tartalmazó blokkból. Az alábbi ElosztottKliens osztályból származó sorok addig
ismétlik a billentyűzetről való olvasást, amígcsak k billentyűt nem nyomunk, mert
String parancs = null;
while((parancs = konzol.readLine()) != null) {
// A Hos CORBA objektum kilép a labirintusból
if("k".equals(parancs))
break;
}
annak hatására a break utasítás kilépteti a végrehajtást a ciklusból.
• throws jelzi, hogy egy függvény milyen kivételeket dob. Például a Labirintus osztály a labirintust
állományból felépítő konstruktor függvénye ilyen:
/**
Page 124
Saját világok teremtése és Java
alapok
96 Created by XMLmind XSL-FO Converter.
* Egy megfelelő szerkezetű szöveges állományból elkészít egy új a
* <code>Labirintus</code> objektumot.
*
* @param labirintusFájlNév a labirintust definiáló, megfelelő
* szerkezetű szöveges állomány neve.
* @exception RosszLabirintusKivétel ha a labirintust definiáló állomány
* nincs meg, nem a megfelelő szerkezetű,
* vagy gond van az olvasásával.
*/
public Labirintus(String labirintusFájlNév) throws RosszLabirintusKivétel {
Ezzel befejeztük a kulcsszavak ismertetését, a következő bekezdésben az azonosító szavak tárgyalásával
folytatjuk a Java nyelvtanának ismertetését.
A nem számmal kezdődő és nem kulcsszó szavakat azonosítóknak nevezzük. Az azonosító szavak szerepe -
mint nevük is mutatja - valaminek az azonosítása. Például a változók nevei azonosítók. A korábbi
kódrészletekből idézve a public static final int JÁTÉK_MEGY_HŐS_RENDBEN = 0; sorban a
JÁTÉK_MEGY_HŐS_RENDBEN szó azonosító. A int i = 0; sorban az i egybetűs szó azonosító, vagy mondjuk a
labirintus = new Labirintus(labirintusFájlNév); sorban a labirintus, a Labirintus és a
labirintusFájlNév szavak azonosítók. A labirintus változó értéke egy referencia, az újonnan létrehozott
Labirintus osztálybeli objektum referenciája. Tehát a labirintus azonosító értékével ezt az új objektumot
azonosítja. Ha például ezt az objektumot törölni akarjuk, akkor majd azt írjuk, hogy labirintus = null; azaz
a labirintus azonosítónak azt a null értéket adjuk, ami egyetlen objektumnak sem referenciája. A Labirintus
azonosító egy osztály, a labirintusunkat absztraháló osztály neve, ez az azonosító például nem változó. A
labirintusFájlNév nevű azonosító megint csak egy változó, értéke a labirintus tervrajzát hordozó állomány
nevét tartalmazó karaktersorozat objektum referenciája.
A Java kifejezés szavak lehetnek egyszerűek vagy összetettek.
Az egyszerű kifejezés szavak
• a számok, például a 42, -42 vagy mondjuk a LabirintusVaszon osztálybeli sorokban
// A kijelző törlése
g.setColor(0x00FFFFFF);
g.fillRect(0, 0, getWidth(), getHeight());
// A labirintus kirajzolása
g.setColor(0x00ed7703);
a hexadecimális 0x00FFFFFF a fehér és 0x00ed7703 a vörös=237(10), zöld=119(10), kék=3(10) színeket kódoló
számok.
• a karakterláncok, minden idézőjelek közé zárt szöveg karakterlánc. Például a LabirintusServlet
osztálybeli alábbi sorokban
// A válasz csatornán küldött adatokat a böngésző
// mint html oldalt értelmezze
httpVálasz.setContentType("text/html;charset=UTF-8");
a text/html;charset=UTF-8 egy karakterlánc.
• a logikai igaz és hamis literál értékek, az igaz true és a hamis false.
• az azonosítók (például változó, függvény vagy osztály nevek) is egyszerű kifejezés szavak.
Page 125
Saját világok teremtése és Java
alapok
97 Created by XMLmind XSL-FO Converter.
Az összetett kifejezés szavakat egyszerű és összetett kifejezés szavakból építjük fel a műveleti és zárójelek
felhasználásával.
Az alábbi, a LabirintusKiszolgáló osztályból kiragadott metódus soraiból
/**
* A labirintus sztring reprezentációja.
*
* @return String labirintus sztring reprezentációja.
*/
public String toString() {
return " Idő:" + idő
+ " hős:" + hősök.size()
+ " kincs:" + kincsek.size()
+ " szörny:" + szörnyek.size();
}
a következő kiragadott szó
" Idő:" + idő
+ " hős:" + hősök.size()
+ " kincs:" + kincsek.size()
+ " szörny:" + szörnyek.size();
egy összetett kifejezés szó. Az + jellel összekapcsolt Idő:, hős:, kincs:, szörny: karakterláncokból, az
idő változónévből, mint egyszerű kifejezés szavakból és három változónév.függvénynév() alakú, a .
hivatkozó és a zárójellel összekapcsolt összetett kifejezés szavakból épül fel.
A kifejezés szavak értéket is hordoznak, a program futása során a most tárgyalt kifejezés szó értéke egy
karakterlánc, mondjuk lehet éppen az Idő:325 hős:1 kincs:2 szörny:1.
Az alábbi, a Hisztogram osztályból kiragadott sorokban
// Egy doboz kirajzolása
g.setColor(java.awt.Color.YELLOW);
if(maxDobozÉrték/képMagasság != 0)
g.fillRect(i*dobozSzélesség,
képMagasság-dobozok[i]/(maxDobozÉrték/képMagasság),
képSzélesség/dobozok.length,
dobozok[i]/(maxDobozÉrték/képMagasság));
például a maxDobozÉrték/képMagasság != 0 egy összetett kifejezés szó. A /, a szokásos aritmetikai és a !=
logikai, a nem egyenlőséget vizsgáló jellel összekapcsolt két változónévből és egy számból, a nullából áll.
Értékeljük ki a szóban forgó maxDobozÉrték/képMagasság != 0 összetett kifejezés szót! A két változó
értékének maxDobozÉrték/képMagasság hányadosa egy szám, ha ez nullától különböző, akkor a kifejezés
értéke true, különben false.
1.1.3.1.2. A Java mondattana
Java nyelven, hasonlóan, mint például a beszélt, mondjuk magyar nyelven, sokféle mondat szerkeszthető. A
legegyszerűbb mondatokat - ahogy már fentebb láthattuk is - pontosvessző zárja le, a bonyolultabb mondatokat
pedig az egyszerűbb mondatokból lehet felépíteni.
Page 126
Saját világok teremtése és Java
alapok
98 Created by XMLmind XSL-FO Converter.
Figyelmeztetés a Javaban kezdő Olvasóknak
A következőkben valódi Java forráskódrészletek következnek. Ne próbáljuk erőlködve értelmezni őket,
a minden erőfeszítés nélküli olvasásuk majd az egész kézikönyv feldolgozása után valósul meg - a
szerzők látomása szerint.
A Java nyelv egyszerű mondatai a következők:
• a deklaráló mondatok azt mondják, hogy valami legyen ez, az, ilyen, olyan. Például a Pontmátrix
osztálybeli
/** A pontmátrixot tartalmazó kép. */
java.awt.image.BufferedImage pontmátrixKép;
sorok java.awt.image.BufferedImage pontmátrixKép; deklarációja azt mondja, hogy a
pontmátrixKép nevű változó legyen egy olyan változó, ami értékeként képes hordozni egy
java.awt.image.BufferedImage osztálybeli objektum referenciáját. Röviden a Java programozó azt
mondja majd ilyenkor, hogy a pontmátrixKép egy BufferedImage.
• az értékadó mondatok azt mondják el, hogy valaminek az értéke legyen ez, az, ennyi, annyi. Például a
Sejtautomata osztálybeli alábbi sorok
// Cellaméretek kezdetben
cellaSzélesség = 10;
10 pixel szélesre állítják a sejtautomata kirajzolt celláinak szélességét.
• a metódushívó mondatok függvényt hívnak,
socket.close();
a fenti, a LabirintusKiszolgálóSzál osztálybeli sor lezárja a kommunikációs kaput.
• a deklaráló, értékadó és metódusmondatokat kombinálhatjuk is. A Pontmátrix osztálybeli induló
// A panel mérete
java.awt.Dimension mátrixMéret =
new java.awt.Dimension(800, 800);
mátrixMéret egy Dimension és rögtön megadunk egy ilyen 800x800-as pixelméretű példányt is a megfelelő
konstruktor meghívásával.
De azt is megtehetjük, hogy egyszerűen csak létrehozunk egy példányt
new ElosztottLabirintus();
Page 127
Saját világok teremtése és Java
alapok
99 Created by XMLmind XSL-FO Converter.
mint ahogyan például a ElosztottLabirintus osztályban tettük.
Vagy létrehozunk egy példányt és rögtön hívjuk is egy módszerét:
new Thread(this).start();
mint a LabirintusKiszolgálóSzál osztályban a hálózati kommunikációt elvégző szál elkészítésénél és
indításánál tettük.
Avagy éppen egy metódus visszatérési értékével inicializálunk egy változót, erre az alábbi példát a
// Elkérjük a böngészőbe menő csatornát
java.io.PrintWriter csatornaBöngészőbe = httpVálasz.getWriter();
LabirintusServlet osztályból kicsípve közöltük.
Végül lássunk egy komplexebb példát is:
// Vagy új vagy régi a hős, a hős neve = "hoszt IP : név"
Hős hős = hálózatiLabirintus.hős(socket.getInetAddress().getHostAddress()
+
" : " + játékostól);
amint a LabirintusKiszolgálóSzál osztályban a hős() függvénynek átadott paramétert egy összetett
kifejezés szóval írtuk le, a hős() függvénynek aktuális paraméterként majd ennek a szónak az értéke, például
a 192.168.1.1 : Herkules karaktersorozat objektum referenciája adódik át, amit a HálózatiLabirintus
osztály
/**
* A hálózaton keresztül jelentkező hős elkészítése.
*
* @param név a hős neve (= "hoszt IP : név").
* @return Hős a névhez tartozó, esetleg újonan létrehozott hős.
*/
public Hős hős(String név) {
// Ha már létező hős jelentkezett be újra a játékba
if(hősök.containsKey(név))
return (Hős)hősök.get(név);
// Vagy új játékos jön
else {
// aki még nincs a hősök között
// akkor új hősként létrehozzuk
Hős hős = new Hős(labirintus);
// A hős kezdő pozíciója
hős.sor(9);
hős.oszlop(0);
// Felvétele a hősök közé
hősök.put(név, hős);
return hős;
}
}
Page 128
Saját világok teremtése és Java
alapok
100 Created by XMLmind XSL-FO Converter.
majd a String típusú, név nevű formális paraméterében kap meg.
A Java nyelv összetett mondatai egyszerű és összetett mondatokból néhány kulcsszóval és a kapcsos zárójelek
használatával képezhetők. Főbb összetett mondat típusok a következők:
• a ha-akkor-különben mondatok a program végrehajtásának villa elágazásait írják le. A korábban említett
// Sejt cella kirajzolása
if(rács[i][j] == ÉLŐ)
g.setColor(java.awt.Color.BLACK);
else
g.setColor(java.awt.Color.WHITE);
g.fillRect(j*cellaSzélesség, i*cellaMagasság,
cellaSzélesség, cellaMagasság);
példánál maradva a g Graphics objektum setColor() színbeállító függvénye a java.awt.Color.BLACK
értékkel fog meghívódni, ha a rács i. sorának, j. oszlopának sejtje élő, azaz a rács[i][j] == ÉLŐ feltétel
értéke igaz, és java.awt.Color.WHITE színnel, ha nem igaz.
Ha valamelyik ágon egynél több mondatot szeretnénk írni, akkor ezeket a mondatokat egy kapcsos zárójelek
határolta blokkba kell szerveznünk, mint ahogyan például az alábbi, a HálózatiLabirintus osztálybeli
sorokban láthatjuk:
// Ha már létező hős jelentkezett be újra a játékba
if(hősök.containsKey(név))
return (Hős)hősök.get(név);
// Vagy új játékos jön
else {
// aki még nincs a hősök között
// akkor új hősként létrehozzuk
Hős hős = new Hős(labirintus);
// A hős kezdő pozíciója
hős.sor(9);
hős.oszlop(0);
// Felvétele a hősök közé
hősök.put(név, hős);
return hős;
}
Nézzünk meg egy komplikáltabb példát is alább, a LabirintusVaszon osztályból!
if(kincsKép != null) {
if(!kincsek[i].megtalálva())
g.drawImage(kincsKép,
kincsek[i].oszlop()*téglaSzélesség,
kincsek[i].sor()*téglaMagasság,
javax.microedition.lcdui.Graphics.LEFT
|javax.microedition.lcdui.Graphics.TOP);
} else {
// Ha már megvan a kics, akkor szürkébbel rajzoljuk
if(kincsek[i].megtalálva())
g.setColor(0x00d2cfb7);
else // Különben sárgábbal
g.setColor(0x00fbe101);
g.fillRect(kincsek[i].oszlop()*téglaSzélesség,
kincsek[i].sor()*téglaMagasság,
téglaSzélesség/2, téglaMagasság);
Page 129
Saját világok teremtése és Java
alapok
101 Created by XMLmind XSL-FO Converter.
}
• a ha-különben-ha mondatok hasonlóak az előzőekhez, de több ága lehet a villának, a lehetőségek közül egy,
az első fog lefutni:
// A kurzor gomboknak megfelelő irányba lépéssel
if ((billentyű & LEFT_PRESSED) != 0) {
hős.lépBalra();
} else if ((billentyű & RIGHT_PRESSED) != 0) {
hős.lépJobbra();
} else if ((billentyű & UP_PRESSED) != 0) {
hős.lépFöl();
} else if ((billentyű & DOWN_PRESSED) != 0) {
hős.lépLe();
}
ebben a LabirintusVaszon osztálybeli példában a kapcsos zárójelekre nem is lett volna szükség, mert
minden villa ágon csupán egyetlen utasítás van, de soha ne szégyelljünk bőven bezárójelezni egy kódot.
Mivel a programok tipikusan bonyolódni szoktak, a bőséges zárójelezés sokszor egyértelműbbé és
átláthatóbbá teszi a forrást, ami egyben könnyedebb továbbfejleszthetőséget is jelent! Visszatérve a példára,
az else ágat itt elhagytuk.
• az ellenőrzött mondatok végrehajtása speciális, mert ha a mondat értelmezése során valamilyen kivétel
keletkezik, akkor azt kezelhetjük. Például a Labirintus osztály kódjából kiragadott alábbi kódrészletben
while(sor.startsWith("//"))
sor = szövegesCsatorna.readLine();
try {
kincsekSzáma = Integer.parseInt(sor);
sor = szövegesCsatorna.readLine();
szörnyekSzáma = Integer.parseInt(sor);
sor = szövegesCsatorna.readLine();
szélesség = Integer.parseInt(sor);
sor = szövegesCsatorna.readLine();
magasság = Integer.parseInt(sor);
szerkezet = new boolean[magasság][szélesség];
} catch(java.lang.NumberFormatException e) {
throw new RosszLabirintusKivétel("Hibás a kincsek, szörnyek száma,
szélesség, magasság megadási rész.");
}
addig olvasunk be sorokat a szövegesCsatorna csatornáról, amíg a // megjegyzés jellel kezdődő sorok el
nem fogynak a labirintusunkat leíró
//
// labirintus.txt
//
// DIGIT 2005, Javat tanítok
Page 130
Saját világok teremtése és Java
alapok
102 Created by XMLmind XSL-FO Converter.
// Bátfai Norbert, [email protected]
//
// A labirintus szerkezetét megadó állomány, szerkezete a következő:
// a kincsek száma
// a szörnyek száma
// a labirintus szélessége
// magassága
// fal=1 járat=0 ...
// .
// .
// .
6
3
10
10
0 0 0 1 0 1 0 1 1 1
...
állományunk elejéről. A try { vezeti be a megfigyelt forráskód blokk kezdetét. Itt olvassuk be a 6, 3, 10, 10
számokat. Ha a második 10-et elírjuk például q0-ra, akkor a magasság = Integer.parseInt(sor); sor
értelmezésekor egy hiba keletkezik, egy NumberFormatException kivétel objektum, ami után a vezérlés már
nem kerül a következő, a szerkezet = new boolean[magasság][szélesség]; sorra, hanem az ennek
megfelelő } catch(java.lang.NumberFormatException e) { kivételkezelő ágon folytatódik, ahol
megtörténik a kivétel kezelése. Jelen esetünkben ez a kivétel tovább dobását jelenti a jelen kódunkat hívó
függvénynek... a kivételkezelés részletes tárgyalását majd a Kivételkezelés című pontban folytathatja a kedves
Olvasó.
• a ciklus mondatokkal a ciklusokat írjuk le, csak az előírt lépésszámú ciklusra kitérve a korábbi
for(Kincs kincs : kincsek) {
if(kincs.megtalalt(hős))
hős.megtalaltam(kincs);
}
példában a ciklus kapcsos zárójelek közé zárt magja vagy törzse a kincsek, egyébként Kincs objektumokat
tartalmazó lista objektum minden tagjára lefut. Gyakorlatilag minden kincstől megkérdezzük, hogy rátalált-e
a hősünk?
A szintúgy korábbi kódtöredékben
// Szörnyek létrehozása
szörnyek = new Szörny[szörnyekSzáma];
for(int i=0; i<szörnyek.length; ++i)
szörnyek[i] = new Szörny(this);
az i ciklusváltozót a 0 kezdőértékkel inicializáljuk, ha a i<szörnyek.length igaz, akkor ezzel az i=0
értékkel végrehajtjuk a magot, majd következik a ++i, azaz az i értékének eggyel való megnövelése, miután
újra megvizsgáljuk a i<szörnyek.length feltételt. Ha igaz, végrehajtjuk a magot , s így tovább, egészen
addig, amíg hamis nem lesz, mert akkor már nem hajtjuk végre a magot és a vezérlés a ciklus mondat utáni
következő mondatra kerül.
• a függvény mondatok egy függvényt definiálnak. A függvény nevét melléknevek előzik meg, majd kerek
zárójelek között a formális paraméterei szerepelnek, amit a kapcsos zárójelek közé zárt függvénytörzs vagy
test követ. A LabirintusMIDlet osztály
Page 131
Saját világok teremtése és Java
alapok
103 Created by XMLmind XSL-FO Converter.
/**
* A MIDletet felfüggesztő életciklus metódus, azaz mit tegyünk,
* ha egy bejövő hívás vagy SMS megzavarja a programunk futását?
* (Most semmit, mert csupán üres testes implementációját adtuk a
* függvénynek.)
*/
public void pauseApp() {}
függvénye az üres paraméterlistára () és az üres testre {} is példát mutat.
Nem ennyire triviális példa a Pontmátrix osztály
public void pillanatfelvétel(String fájlNév) {
// png formátumú képet mentünk
try {
javax.imageio.ImageIO.write(pontmátrixKép, "png",
new java.io.File(fájlNév));
} catch(java.io.IOException e) {
e.printStackTrace();
}
}
függvénye, melynek már van egyetlen formális paramétere, a String fájlNév, és a törzse sem üres: png
képformátumban elmenti az osztály pontmátrixKép BufferedImage példányát a paraméterként kapott
állománynéven a javax.imageio.ImageIO osztály statikus write() függvényével. Érdekességként
megjegyezhetjük, hogy a formális paramétert a hívás helyén úgy aktualizáljuk, hogy az átadott aktuális
paraméter karaktersorozat objektumot a felhasználói felület egy állománykiválasztó párbeszédablakában
jelöljük ki:
} else if("Mentés...".equals(menü)) {
javax.swing.JFileChooser betöltő = new javax.swing.JFileChooser();
betöltő.showSaveDialog(getContentPane());
pillanatfelvétel(betöltő.getSelectedFile().getAbsolutePath());
• az osztály mondatok a legbővebb összetett mondatok, velük definiáljuk az osztályokat. Az osztály nevét
melléknevek előzik meg, közvetlenül előtte a class áll, illetve az osztálynév után néhány szóba jöhető
kulcsszó után, a kapcsos zárójelek között következik az osztály törzse vagy teste. Egyszerű példaként álljon
itt a korábbi Szereplő osztály:
public class Szereplő {
Az öröklés, kiterjesztés jelzésére pedig például a Hős osztály:
public class Hős extends Szereplő {
Az Olvasó az öröklődéssel Az osztályok fejlődése: az öröklődés című pontban ismerkedhet majd meg
részletesen. Végül tekintsünk egy komplexebb példát a LabirintusKiszolgáló osztályt!
Page 132
Saját világok teremtése és Java
alapok
104 Created by XMLmind XSL-FO Converter.
public class LabirintusKiszolgáló
extends javattanitok.labirintus.TöbbHősösLabirintus
implements LabirintusOperations, Runnable {
Kiterjesztjük a másik csomagbeli TöbbHősösLabirintus osztályt és implementáljuk a
LabirintusOperations és a Runnable interfészeket.
1.1.3.2. A Java nyelv és a Java programozás között
Alapvető fontosságú - az iménti pontok terminológiájával: függvény - mondat lesz a következő.
függvényNév(változók felsorolása) {
}
Az ilyen mondatokat függvényeknek vagy metódusoknak nevezzük.
A függvényNév a függvény neve, ezt követi egy kerek zárójelek közötti változó-felsorolás - a függvény
formális paramétereinek megadása - ami ugyan akár el is maradhat, majd egy kapcsos zárójelpár következik,
ami további mondatokat tartalmazhat.
A függvények tipikus hivatása, hogy kiszámoljanak és visszaadjanak hívóiknak valamilyen típusú eredményt.
Ezt így írjuk le:
EredményTípus függvényNév(változók felsorolása) {
}
Ha a függvény nem ad vissza értéket a hívónak, akkor azt a void kulcsszó szerepeltetésével kell jeleznünk:
void függvényNév(változók felsorolása) {
}
Még a függvények leírásánál is alapvetőbb mondatok az osztályok definícióját leíróak - mint ahogyan a
Labirintus osztály kapcsán az imént láttunk erre egy konkrét példát - ezeknek a mondatoknak általános
szerkezete a következő:
class OsztályNév {
}
A kapcsos zárójelek közé jöhetnek az osztály által leírt dolog tulajdonságait megadó egyszerű Java nyelvű
mondatok, vagy függvények.
1.1.4. Vissza az OO-hoz
1.1.4.1. Objektumok létrehozása: a konstruktor
Page 133
Saját világok teremtése és Java
alapok
105 Created by XMLmind XSL-FO Converter.
Egy osztály függvényei között a konstruktornak nevezett függvények speciális rendeltetésűek, feladatuk az
osztályból származó objektumok felépítése.
A konstruktor függvény neve megegyezik az osztály nevével:
Labirintus(int szélesség, int magasság) {
}
Vegyük észre, hogy a konstruktor függvényeknek nincs valamely kiszámolt értéket visszaadó visszatérési
értéke, s a void kulcsszó sem szerepel a függvény fejének leírásában.
A paraméterként kapott két egész szám a létrehozandó új Labirintus objektum szélessége és magassága.
Tegyük fel, hogy van egy programunk, ami használni akar egy labirintust és a program éppen most érkezik
ahhoz a ponthoz, ahol szüksége van a labirintusra! Ekkor példányosít a Labirintus osztályból, azaz le fog futni
a Labirintus osztály konstuktora. Gondoljunk bele, mit kell végrehajtania a konstruktornak?
A paraméterként kapott szélességet és magasságot be kell állítania a létrehozandó új labirintus objektumban,
majd létre kell hoznia az ennek a két számnak megfelelő méretű labirintus szerkezetet. A kapott szélesség és
magasság tulajdonság beállítása azért fontos feladat, mert a létrehozandó új labirintus objektumnak ezt a két
számot tudnia kell magáról, mindig tudnia, nem csupán a megfelelő méretű labirintus szerkezet létrehozásakor.
Írjuk le ezt Java nyelven:
Labirintus(int szélesség, int magasság) {
this.szélesség = szélesség;
this.magasság = magasság;
szerkezet = new boolean[szélesség][magasság];
}
Látható, hogy az osztály definíciójában szereplő szélesség és magasság változónevek megegyeznek a
konstruktor függvény paramétereinek nevével, hogy biztosan a létrehozandó új labirintus objektum szélességét
és magasságát állítsuk be. Ezért az első két értékadásnál a this operátorral minősítjük a változóneveket, ami
annyit jelent, hogy a változónév elé írjuk, hogy this: ezzel megkülönböztetve a függvényben lévő ugyanilyen
nevű változóktól. A this mindig az aktuális, azaz jelen esetben a létrehozandó új objektumot jelenti. Hiszen a
fejlesztés során a programozó szinte mindig valamely osztály viselkedését írja, eközben az jár a fejében, hogy
ebből az osztályból egy példány fog létrejönni, akinek az éppen programozott élethelyzetében a most éppen
Page 134
Saját világok teremtése és Java
alapok
106 Created by XMLmind XSL-FO Converter.
begépelt viselkedést kell mutatnia... - ez az „akinek példány” a this.
A következő, a new operátort használó utasítás létrehozza a megfelelő méretű kétdimenziós logikai tömböt, azaz
a memóriában lefoglal annyi helyet, ahol ez a szerkezet elfér, és logikai celláit automatikusan feltölti hamis
értékekkel. (De az igazi paranoiás programozó az ilyen automatikus értékekkel nem foglalkozik, hanem mindig
maga adja meg azokat... de erre még nem most, hanem majd később látunk példát!)
szerkezet = new boolean[szélesség][magasság];
Saját labirintus osztályunkból ugyancsak a new operátorral tudunk példányosítani. Visszatérve feltevésünkhöz,
hogy van egy programunk, ami használni akar egy labirintust... és a program éppen most érkezik ahhoz a
ponthoz, ahol szüksége van a labirintusra, ekkor éppen a következő sort hajtja végre:
Labirintus labirintus = new Labirintus(10, 10);
Ekkor a kis labirintus azonosítót a létrehozott, új labirintus objektum referenciájának is nevezzük (egészen
pontosan az azonosító, a labirintus nevű változó értéke a referencia). Az objektum tulajdonságaira a
referencia után írt ponttal hivatkozhatunk, azaz a labirintus.szélesség a létrehozott, új labirintus
objektumbeli szélességet jelenti.
Összegezzük eddigi erőfeszítéseinket egyetlen forrásban:
class Labirintus {
int szélesség;
int magasság;
boolean[][] szerkezet;
Labirintus(int szélesség, int magasság) {
Page 135
Saját világok teremtése és Java
alapok
107 Created by XMLmind XSL-FO Converter.
this.szélesség = szélesség;
this.magasság = magasság;
szerkezet = new boolean[szélesség][magasság];
}
static main() {
Labirintus labirintus = new Labirintus(10, 10);
}
}
Java programjaink végrehajtása a main() függvénnyel kezdődik - most egyetlen feladataként - benne
példányosítjuk a létrehozandó új labirintus objektumunkat. Tehát mivel most akarjuk létrehozni a labirintus
objektumot, így nyilván még nem létezik, akkor mégis hogyan futhat a main() metódus? Ezért van a static
módosító kulcsszó! A static kulcsszó azt jelenti, hogy a függvény nem az osztály példányaihoz, hanem
magához az osztályhoz tartozik, azaz anélkül is meg lehet hívni, hogy az őt tartalmazó osztályból
példányosítanánk.
Készítsük most el programunk olyan változatát, amit akár már le is fordíthatunk, futtathatunk:
class Labirintus {
int szélesség;
int magasság;
boolean[][] szerkezet;
Labirintus(int szélesség, int magasság) {
this.szélesség = szélesség;
this.magasság = magasság;
szerkezet = new boolean[szélesség][magasság];
}
public static void main(String[] args) {
Labirintus labirintus = new Labirintus(10, 10);
}
}
A main() függvényt most abban a formában használtuk, amelyben mindig szerepelnie kell, a void kulcsszó
jelzi, hogy a függvény nem ad vissza semmilyen értéket. A public módosító kulcsszó pedig arra utal, hogy
hogyan látszik az osztályunkon kívülről a függvény. A public azt jelenti, hogy mindenhonnan látszik. A
finomságokat majd később részletezzük, most annyit jegyezzünk meg, hogy a main() függvényt mindig így
használjuk. Azt persze még megemlíthetjük, hogy más formában nem is használhatjuk, mert eltérés esetén az
osztályunkat nem tudjuk lefordítani. A main() paraméterét a következő feladat után tárgyaljuk.
Tehát itt az idő, próbáljuk is ki ezt az osztályt!
C:\...> javac Labirintus.java
C:\...> java Labirintus
Page 136
Saját világok teremtése és Java
alapok
108 Created by XMLmind XSL-FO Converter.
Sok látványos dolog nem történt, a program indítása után gyorsan visszaadta a promptot és kész. Megérte
feldolgozni ezt a sok oldalt eddig, ennyiért? Hogyne, hiszen pici módosítással szóra bírhatjuk a Labirintus
osztálybeli objektumunkat. Módosítsuk így az indítófüggvényt:
public static void main(String[] args) {
Labirintus labirintus = new Labirintus(10, 10);
System.out.println(labirintus.magasság);
}
A Labirintus.java forrásállomány módosítása után azt újra lefordítva és az osztályt futtatva:
C:\...> javac Labirintus.java
C:\...> java Labirintus
10
Sikerrel kiírattuk a labirintus magasságát.
1.1.4.1.1. Példánytag elérése osztályszintű függvényből feladat
A fordítás-futtatás-(bosszankodás) munkamenet begyakorlásaképpen, írassuk ki a labirintus szélességét is,
módosítsuk a main() függvényt így:
public static void main(String[] args) {
Labirintus labirintus = new Labirintus(10, 10);
System.out.println(labirintus.szélesség);
System.out.println(labirintus.magasság);
}
Mi történik az alábbi módosítás esetén, azaz ha a méretet megadó változóneveket nem a labirintus példány
referenciája után írjuk, hanem csak úgy egyszerűen ki akarjuk íratni őket:
public static void main(String[] args) {
Labirintus labirintus = new Labirintus(10, 10);
System.out.println(szélesség);
System.out.println(magasság);
}
Ekkor a futtatásig már el sem jutunk, mert a forrás nem fordul! A NetBeans környezet rögtön a leírása után jelzi
a hibát, a parancssort használó pedig ezt fogja látni, amikor megpróbálja lefordítani a forrást:
C:\Documents and Settings\norbi> javac Labirintus.java
Page 137
Saját világok teremtése és Java
alapok
109 Created by XMLmind XSL-FO Converter.
Labirintus.java:19: non-static variable szélesség cannot be referenced from
a static context
System.out.println(szélesség);
^
Ez fontos hiba, amibe eleinte sokszor belefut a kezdő Java OO programozó Olvasó, de gyorsan ki lehet nőni.
Pontosan azt mondja, hogy a Labirintus.java forrásállományom 19. sorában a példányokhoz, és nem az
osztályhoz tartozó változót osztályhoz, és nem példányhoz kapcsolódó környezetben akarok használni. Ez
valóban nem értelmezhető, hiszen az indító függvényem egyetlen példányhoz sem tartozik, sőt direkt azért van,
mert amikor ő indul, akkor még nincsenek példányok, majd pont ő hozza létre az elsőt vagy az elsőket. Ha pedig
nincs példányom, akkor hogyan is akarhatom kiíratni egy példány szélességét! Mindaddig, amíg ezt a hibát nem
érezzük logikusnak, filozofáljunk el a példány és az osztály különbözőségén!
Visszatérve a példa fősodrához: a létrehozandó új labirintus objektum már tudja magáról saját méretét, megvan
az ennek a méretnek megfelelő méretű labirintus szerkezetet hordozni képes adatszerkezet, azaz a megfelelő
kétdimenziós logikai tömb, de maga a szerkezet még nincs meg. Megadhatnánk ezt téglánként:
Labirintus(int szélesség, int magasság) {
this.szélesség = szélesség;
this.magasság = magasság;
szerkezet = new boolean[szélesség][magasság];
szerkezet[0][0] = true; szerkezet[0][1] = true; ...
szerkezet[1][0] = true; szerkezet[1][1] = false; ...
.
.
.
}
De ezzel több probléma is van. Láthatóan nagy méret esetén egyik a nehézkesség, nem is beszélve a labirintus
szerkezetének módosíthatóságáról. A másik alapvetőbb, hogy előre a forrásszöveg írásakor nem tudjuk, hogy a
...-ok helyére hány oszlopnyi és hány sornyi konkrét szerkezeti cella megadás kellene. Mert ez nem dől el a
fordítási, hanem csak a futási időben, amikor a Java Virtuális Gép végrehajtja a
Labirintus labirintus = new Labirintus(10, 10);
példányosítást, a konkrét labirintus létrehozását.
Válasszunk más megoldást! Nem túl általános, de annál egyszerűbb megoldásként lássuk el az osztályt egy
olyan konstruktorral is, ami fix méretű labirintust csinál. Mivel a méret fix, így a konstruktornak nem
szükségesek paraméterek:
Labirintus() {
szerkezet = new boolean[][]{
{false,false,false,true,false,true,false,true,true,true},
{false,false,false,false,false,false,false,false,false,false},
{true,false,true,false,true,false,true,false,true,false},
{false,false,false,false,true,false,true,false,false,false},
{false,true,true,false,false,false,true,true,false,true},
{false,false,false,false,true,false,false,false,false,false},
{false,true,false,false,false,true,false,true,true,false},
{false,false,false,true,false,true,false,true,false,false},
Page 138
Saját világok teremtése és Java
alapok
110 Created by XMLmind XSL-FO Converter.
{false,true,false,false,false,false,false,false,false,true},
{false,false,false,false,true,false,false,false,true,true}
};
magasság = szerkezet.length;
szélesség = szerkezet[0].length;
}
Most a logikai tömb elkészítésekor azt is megmondtuk, hogy a tömb logikai celláit milyen logikai értékkel
akarjuk feltölteni. Amikor a tömb elkészült, a szélességet és a magasságot már tőle kérjük el:
magasság = szerkezet.length;
szélesség = szerkezet[0].length;
Hogy teljesen világos legyen, jöjjön a következő ábrán egy kis tömbológia.
A kétdimenziós tömb egydimenziós tömbökből áll, példánknál maradva: a szerkezet egy olyan tömb, aminek
elemei (az ábrán a sorok) megint csak tömbök, így jön ki a két dimenzió.
A
szerkezet.length;
megadja a tömb méretét, hogy hány eleme van, azaz most, hogy hány sora van, hogy hány tömb eleme van. A
szerkezet[0].length;
megadja az első (azaz a 0.) elem méretét, az elem most tömb, mérete annyi, ahány eleme van, azaz most, ahány
logikai érték van egy sorban: tehát az oszlopok száma.
Begyakorolandó a kétdimenziós tömb bejárását a Vezérlési szerkezetek című pontban azzal folytatjuk, hogy
kiíratjuk az argumentum nélküli konstruktorral készített tömb szerkezetét.
A konstruktorokhoz visszatérve, válasszunk további más megoldást! A labirintus elképzelése során, a
négyzethálós lapon egy kétdimenziós tömb alakjában rajzoltuk le a labirintus szerkezetét. Készítsünk el egy
ennek a rajznak megfelelő szöveges állományt és osztályunkat lássuk el egy olyan konstruktorral, ami képes ezt
beolvasni! Ekkor majd így példányosítjuk a labirintus objektumunkat:
Page 139
Saját világok teremtése és Java
alapok
111 Created by XMLmind XSL-FO Converter.
Labirintus labirintus = new Labirintus("labirintus.txt");
A szélességgel, magassággal kapcsolatban nem adunk át infót a konstruktornak, hiszen ezt a két számot a
labirintus rajzából, azaz majd az állományból is meg tudja állapítani maga a konstruktor is. Viszont
paraméterként adjuk a konstruktornak a tervrajznak megfelelő szöveges állomány nevét. Ennek az új
konstruktornak az elkészítését A labirintust állományból felépítő konstruktor című pontjában folytatjuk.
1.1.4.1.2. Saját szereplők feladat
A labirintusunk kincseit, szörnyeit és hősünket az a közös tulajdonság jellemzi, hogy valahol vannak a
labirintusban. Helyzetüket egy számpárral írhatjuk le:
int oszlop;
int sor;
ennek megfelelően, például a Szereplő nevű Java osztályba foglalva írhatjuk, hogy
class Szereplő {
int oszlop;
int sor;
}
Milyen lényeges jellemzőik legyenek még a szereplőknek? Azaz hogyan alakítsuk ki, álmodjuk meg a játék
világát, a labirintus játék programunk mikrokozmoszát? Milyen szereplőink lesznek, avagy amiért szép a
játékfejlesztés, e kérdés itt így is feltehető: milyen szereplőink legyenek?
A kincsnek mondjuk legyen értéke:
int érték;
a hősnek pontszáma, amiben a már összeszedett kincsek értékeit gyűjti majd:
int megtaláltÉrtékek;
Adott kincsről azt is tudnunk kell, hogy megtalálta-e már a hős vagy sem?
boolean megtalálva;
S lehet hosszan tervezgetni, rajzolgatni..., megint áttervezni, az egészet a papírkosárba dobni és párszor ezt újra
elismételni.
Page 140
Saját világok teremtése és Java
alapok
112 Created by XMLmind XSL-FO Converter.
Ha azt akarjuk, hogy a Kincs osztályunk objektumai minden olyan jellemzővel rendelkezzenek, mint amikkel
az általánosabb Szereplő osztálybeli objektumok (vagy aki fonákkal gondolkozik: a Kincs nem más, mint egy
speciális Szereplő), akkor a Kincs osztállyal ki kell terjesztenünk a Szereplő osztályt, ekkor Javaban ezt
írjuk:
class Kincs extends Szereplő {
int érték;
boolean megtalálva;
}
Ekkor a Kincs osztályból példányosított objektumok a Szereplő osztály tulajdonságaival is rendelkezni
fognak, így végeredményben egy Kincs osztálybeli objektum a következő tulajdonság tagokkal fog rendelkezni:
int oszlop;
int sor;
int érték;
boolean megtalálva;
Ilyenkor azt is szoktuk mondani, hogy a Kincs osztály gyermeke a Szereplő osztálynak, vagy megfordítva,
hogy a Szereplő osztály őse a Kincs osztálynak.
A szereplők feladat arról szól, hogy mindenki képzelje el saját labirintusa szereplőit! Milyen további
tulajdonságokat tudunk elképzelni? Válaszunkat a saját Kincs, Hős, Szörny osztályainkban fogalmazzuk meg!
Mi magunk ennek a feladatnak a megoldását Az osztályok fejlődése: az öröklődés című fejezetben adjuk meg,
folytatva az itt megkezdett öröklődéssel kapcsolatos kérdések boncolgatását.
Milyen konstruktorral szereljük fel a Szereplő osztályt? Először is, a labirintus szereplőit el kell valahová
helyeznünk a labirintusban, ezt nehezíti, hogy nem tehetjük őket bárhová, legalábbis a falba nem, hanem csak
járatba, azaz a labirintus olyan pozíciójára, ami nem fal! Ebből a gondolatból következően már érezzük, hogy a
Szereplő és a Labirintus között van egy erős kapcsolat. A programtervező feladata ezt a kapcsolatot
valamilyen formában elkészíteni. Mi most azt a megoldást választjuk, hogy a Szereplő osztálybeli
objektumnak átadjuk annak a Labirintus osztálybeli objektumnak a referenciáját, amibe éppen bele akarjuk
helyezni, azaz a Szereplő osztálybeli konstruktorunkat így írjuk meg:
class Szereplő {
int oszlop;
int sor;
Labirintus labirintus;
Szereplő(Labirintus labirintus) {
this.labirintus = labirintus;
szereplőHelyeKezdetben();
}
}
1.1.4.2. Objektumok kommunikációja: a metódushívás
Page 141
Saját világok teremtése és Java
alapok
113 Created by XMLmind XSL-FO Converter.
A tipikus OO program az egymással kölcsönható objektumok összessége. Az objektumok közötti kölcsönhatás,
kommunikáció az objektumok metódusainak hívásán keresztül valósul meg. Ha egy objektumnak üzenni
akarunk, ha egy objektum valamely szolgáltatását akarjuk igénybe venni vagy egészen egyszerűen szólva meg
akarjuk hívni egy objektum valamely függvényét, metódusát, akkor nem kell mást tennünk, mint leírni az
objektum referenciáját, azt követően egy pontot, majd a metódus nevét magát. Például korábban tárgyaltuk,
hogy a System.out, azaz a System osztálybeli out tag egy PrintStream osztálybeli objektum referenciáját,
azaz a mindenkori programunkhoz rendelt sztenderd kimenő csatorna objektum referenciáját tartalmazza. Ha
ennek az objektumnak akarok üzenni, például mondjuk azt, hogy írja ki a Helló, Világ! szöveget, akkor a
PrintStream osztály println() függvényét kell használnom a referencia utáni pontot követően írva tehát:
System.out.println("Helló, Világ!");
Nézzünk még néhány további példát a metódushívásra a Labirintus osztályból! Az i. kincstől megkérdezzük,
hogy megtalálta-e a hős.
// A hős rátalált valamelyik kincsre?
if(kincsek[i].megtalált(hős))
hős.megtaláltam(kincsek[i]);
Ha igen, akkor a hősnek átadjuk a megtalált kincset. A következő ciklusban az összes szörny lép egyet a hős
felé:
for(int i=0; i < szörnyek.length; ++i) {
szörnyek[i].lép(hős);
1.1.4.3. Az osztályok fejlődése: az öröklődés
Folytatjuk a játék világát absztraháló, a Saját szereplők feladat feladatban megkezdett osztályhierarchia
felépítését. Ott hagytuk abba, hogy a hős a megtalált kincsek értékeit a megtaláltÉrtékek változójában
gyűjtögeti majd.
class Hős extends Szereplő {
int megtaláltÉrtékek;
Hős(Labirintus labirintus) {
super(labirintus);
megtaláltÉrtékek = 0;
}
public void megtaláltam(Kincs kincs) {
megtaláltÉrtékek += kincs.érték();
}
A Hős osztály példányát felépítő Hős(Labirintus labirintus) konstruktor első dolga az ős, a Szereplő
osztály Szereplő(Labirintus labirintus) konstruktorának meghívása - ezt eredményezi a
super(labirintus); hívást tartalmazó első sor. Az ős korábban bemutatott konstruktora beállítja a
labirintus-t és elhelyezi a hős szereplőt valahová ebbe a labirintusba. Majd a Hős osztály megtaláltÉrtékek
tagja kapja meg a 0 kezdőértékét. A megtaláltÉrtékek tagot az osztály megtaláltam() módszerével növeli a
paraméterként jövő, épp megtalált kincs értékével.
Page 142
Saját világok teremtése és Java
alapok
114 Created by XMLmind XSL-FO Converter.
Építsük tovább a hősünket absztraháló osztályt! Legyen a hősnek néhány élete, kezdetben mondjuk
ÉLETEK_SZÁMA darab, és minden esetben, amikor hősünket megeszik a labirintusban, ez az érték csökkenjen
eggyel! Ennek a tulajdonságnak a hordozására felveszünk egy életekSzáma egész típusú változót, amit az
ÉLETEK_SZÁMA = 5 konstans értékkel inicializálunk:
class Hős extends Szereplő {
int megtaláltÉrtékek;
public static final int ÉLETEK_SZÁMA = 5;
int életekSzáma = ÉLETEK_SZÁMA;
Hős(Labirintus labirintus) {
super(labirintus);
megtaláltÉrtékek = 0;
}
public void megtaláltam(Kincs kincs) {
megtaláltÉrtékek += kincs.érték();
}
public boolean megettek() {
if(életekSzáma > 0) {
--életekSzáma;
return false;
} else
return true;
}
Az osztály a megettek() nevű viselkedésében kezeli a felvett életekSzáma tagját. A függvény visszatérési
értékével jelzi, hogy él-e még egyáltalán a hős.
A Hős osztály végleges formáját a A Hős osztály című pontban tanulmányozhatja az Olvasó.
A például a [SOMMERVILLE KÖNYV] könyvben bemutatott UML jelöléseit használva az alábbi
osztálydiagrammal foglaljuk össze és fejlesztjük tovább terveinket.
Page 143
Saját világok teremtése és Java
alapok
115 Created by XMLmind XSL-FO Converter.
A kézikönyvhöz készített osztályok közötti összes öröklési kapcsolatot könnyen megtalálhatja az érdeklődő
Olvasó, ha elugrik A csomagok szervezése című pontra, ahol az osztálynevek (a javadoc paranccsal készített
Java dokumentáció mintájára) tabulált szedésével jeleztük az öröklődést:
java.lang.Object
javattanitok.labirintus.Labirintus
javattanitok.labirintus.GenerikusLabirintus
javattanitok.labirintus.TöbbHősösLabirintus
javattanitok.labirintus.Szereplő
javattanitok.labirintus.Hős
javattanitok.labirintus.Kincs
javattanitok.labirintus.Szörny
java.lang.Throwable (implements java.io.Serializable)
java.lang.Exception
javattanitok.labirintus.RosszLabirintusKivétel
vagy ugyaninnen, de néhány oldallal későbbről idézve:
javax.microedition.lcdui.game.GameCanvas
javattanitok.LabirintusVaszon (implements java.lang.Runnable)
javattanitok.HálózatiLabirintus (implements java.lang.Runnable)
javattanitok.KorbásLabirintus
javattanitok.TávoliLabirintus (implements javattanitok.TávoliHősíthető)
javax.servlet.http.HttpServlet
javattanitok.LabirintusServlet
Page 144
Saját világok teremtése és Java
alapok
116 Created by XMLmind XSL-FO Converter.
A labirintus példák absztrahálta világok bonyolódásával a világokat leíró osztályokat is fejlesztenünk kellett
egészen addig, hogy a hálózati labirintus világában az addig használt labirintus már nem volt megfelelő. Mivel
ebben a világban egy hős halála immár nem jelentette egyben a labirintus játék végét is, hiszen a hálózaton
keresztül jövő többi hős egyikük halálától függetlenül még nyugodtan bolyongana tovább a labirintusban. A
hálózati labirintus részletes kifejtését a TCP/IP - Hálózati Labirintus című pontban találjuk meg.
Természetes az a gondolat, hogy ennek a bonyolultabb világnak az absztrahálásához szükségünk lenne a
szokásos labirintusra, de annyi módosítással, hogy most már a bolyongó hős halála ne jelentse a labirintus
állapotának drasztikus megváltozását. A megoldás, hogy a régi osztály kiterjesztésével új osztályt készítünk,
azaz új osztályunkat a TöbbHősösLabirintus osztályt a Labirintus osztályból örököltetjük, majd a
Labirintus public int bolyong(Hős hős) viselkedését a TöbbHősösLabirintus osztályban
felüldefiniáljuk. Figyelje meg a kedves Olvasó, hogy a A TöbbHősösLabirintus osztály című pontban bemutatott
kód mennyivel rövidebb a A Labirintus osztály című pontba foglalt ős labirintus kódjától!
Ha összehasonlítjuk az ős public int bolyong(Hős hős) függvényének megfelelő
for(int i=0; i < szörnyek.length; ++i) {
szörnyek[i].lép(hős);
if(szörnyek[i].megesz(hős)) {
játékÁllapot = JÁTÉK_MEGY_MEGHALT_HŐS;
if(hős.megettek())
játékÁllapot = JÁTÉK_VÉGE_MEGHALT_HŐS;
return játékÁllapot;
}
}
részletét a származtatott osztály public int bolyong(Hős hős) megfelelő részletével:
for(int i=0; i < szörnyek.length; ++i) {
szörnyek[i].lép(hős);
if(szörnyek[i].megesz(hős)) {
if(hős.megettek())
// De ez a játék vége csak a hős végét
// jelenti, a labirintusét nem!
return JÁTÉK_VÉGE_MEGHALT_HŐS;
else
return JÁTÉK_MEGY_MEGHALT_HŐS;
}
}
akkor láthatjuk, hogy a felüldefiniált viselkedés már nincs hatással a játék állapotára, azaz új osztályunkban egy
hős halála a labirintus világára már nincs hatással.
1.1.4.3.1. Többalakúság
A többalakúságra (polimorfizmusra) példát láthatunk, ha a HálózatiLabirintus osztályban a labirintusunkat
így vesszük fel:
public class HálózatiLabirintus implements Runnable {
Page 145
Saját világok teremtése és Java
alapok
117 Created by XMLmind XSL-FO Converter.
/** A játék aktuális labirintusa, minden hálózati hős ebben mozog. */
// TöbbHősösLabirintus labirintus;
Labirintus labirintus;
viszont a továbbfejlesztett TöbbHősösLabirintus osztálybeli objektumként hozzuk létre, azaz a
HálózatiLabirintus konstruktorban így példányosítjuk a labirintust:
public HálózatiLabirintus(String labirintusFájlNév) throws
RosszLabirintusKivétel {
// A labirintus elkészítése állományból
labirintus = new TöbbHősösLabirintus(labirintusFájlNév);
példánkban a többalakúság az, hogy a Labirintus labirintus nem egy Labirintus osztálybeli objektum,
hanem a Labirintus osztály egy leszármazottjabeli, a TöbbHősösLabirintus osztálybeli objektum. Ha ennek
a labirintus objektumnak meghívjuk a public int bolyong(Hős hős) metódusát, akkor a felüldefiniáló,
azaz a TöbbHősösLabirintus osztálybeli public int bolyong(Hős hős) függvény fog lefutni.
Melyik metódus hívódik?
Szúrjunk be két logoló sort a Labirintus (szülő) és a TöbbHősösLabirintus (gyermek) osztálybeli
public int bolyong(Hős hős) függvénybe:
System.out.println("A gyermekbeli hívódott");
System.out.flush();
System.out.println("A szülőbeli hívódott");
System.out.flush();
majd a HálózatiLabirintus osztályt futtatva győződjünk meg róla, hogy a gyermekbeli módszer
hívódik!
1.1.5. Mi történik a metódusokban?
Az OO program osztályainak, az osztályok közötti kapcsolatok megállapításának folyamata inkább egy
szervezési és átfogó jellegű stratégiai tervezési feladat. Ezzel szemben az osztály metódusainak implementálása
taktikai jellegű. Ez az a hely, ahová a klasszikus imperatív programozó visszaszorult az OO paradigmaváltás
sikere után. Itt programozzuk be azt, amit csinál a program, az osztály vagy az osztálybeli objektum. Alapvető
imperatív eszközeink ebben, a programunk tipikusan kódolási szakaszában a változók.
1.1.5.1. Típusok és változók
Javaban általában a típusok osztályok, a változók pedig tipikusan valamilyen osztálybeli objektum referenciáját
hordozzák értékként. Kivételt képeznek viszont az úgynevezett primitív típusok, melyek a boolean, byte, char,
double, float, int, long.
Az ilyen típusú változókat osztályok-objektumok nélkül használhatjuk, ők az imperatív programozásban
klasszikusan megszokott változók. Javaban a primitív típusok változóinak értéktartománya meghatározott,
ahogy ezt a következőkben részletesen ismertetjük.
2.1. táblázat - A Java primitív típusai
Típus Minimum Maximum
Page 146
Saját világok teremtése és Java
alapok
118 Created by XMLmind XSL-FO Converter.
Típus Minimum Maximum
boolean false true
byte -27 27-1
char \u0000 \uffff
double 2-1074 (2-2-52)*21023
float 2-149 (2-2-23)*2127
int -231 231-1
long -263 263-1
1.1.5.2. Vezérlési szerkezetek
A vezérlési szerkezetek tipikusan az iteráció és a szelekció megszervezésére adnak lehetőséget programjaink
forrásszövegében. Előbbivel, azaz a ciklusok szervezésével, a for, while és do while kulcsszavak említése
során A Java szófajok, illetve kicsit bővebben A Java mondattana című pontokban ismerkedtünk, itt folytatjuk a
téma tárgyalását. A szelekció kapcsán Javaban az if else, if else if else és switch case szerkezeteket
említhetjük, e konstrukciók megismerését is az imént hivatkozott pontokban kezdtük meg.
1.1.5.2.1. toString()
Elegáns szokás osztályainkat ellátni egy toString() nevű, sztringet visszaadó, paraméter nélküli metódussal.
A visszaadott sztring hivatása, hogy szemléltesse valamilyen értelmes formában az objektumot. Labirintus
osztályunk tekintetében ez a forma lehet a labirintus szélessége, magassága és esetleg a szerkezete is.
public String toString() {
StringBuffer stringBuffer = new StringBuffer();
for(int i=0; i<magasság; ++i) {
for(int j=0; j<szélesség; ++j) {
if(szerkezet[i][j])
stringBuffer.append("X");
else
stringBuffer.append("+");
}
stringBuffer.append("\n");
}
return stringBuffer.toString();
}
A külső
for(int i=0; i<magasság; ++i) {
Page 147
Saját világok teremtése és Java
alapok
119 Created by XMLmind XSL-FO Converter.
ciklus i indexe a tömb sorain fut majd végig (nullától a magasság-1 értékig), s egy adott i. soron belül ebbe a
külső ciklusba ágyazott belső
for(int j=0; j<szélesség; ++j) {
ciklus j indexe a tömb oszlopain fut majd végig (nullától a szélesség-1 értékig). Ennek megfelelően a belső
ciklus magjában az
if(szerkezet[i][j])
...
else
...
elágazó utasítás, ha az i. sor j. oszlopában igaz érték (azaz nálunk fal és ennek megfelelően az if utasítás
fejében lévő szerkezet[i][j] kifejezés szó értéke igaz) van, akkor az X-et nyomtató ágra, ha hamis, akkor a +
jelet nyomtató ágra viszi a vezérlést.
Az így implementált toString() függvényünk tipikusan a következő formájú sztringet adja vissza, melyen a
labirintus felépítményét tanulmányozhatjuk, ahol az X betű jelzi a járatot és a + betű a falat.
+++X+X+XXX
++++++++++
X+X+X+X+X+
++++X+X+++
+XX+++XX+X
++++X+++++
+X+++X+XX+
+++X+X+X++
+X+++++++X
++++X+++XX
1.1.5.2.2. A labirintust állományból felépítő konstruktor
Itt egyben folytatjuk az Objektumok létrehozása: a konstruktor című fejezetben megkezdett - olyan
konstruktorral szereljük fel a Labirintus osztályt, ami egy állományból is képes felépíteni a labirintus
szerkezetét - példánkat. A labirintus szerkezetét leíró szöveges állomány nevét a konstruktor paraméterként
kapja meg, labirintusFájlNév nevű formális paraméterében.
public Labirintus(String labirintusFájlNév) {
}
Először kitaláljuk annak az állománynak a szerkezetét, amiben leírjuk a labirintusunkat: mondjuk a dupla
perjelek utáni sorokkal nem foglalkozunk, itt lehetnek majd a kommentek. Aztán jöjjön négy szám: a kincsek
száma, a szörnyek száma és a labirintus szélessége, magassága. Végül következzék maga a labirintus 0, 1
jegyekkel lekódolva: az 1 jelentse a falat, a 0 a járatot! S íme a mi példánk erre a szöveges állományra:
Page 148
Saját világok teremtése és Java
alapok
120 Created by XMLmind XSL-FO Converter.
//
// labirintus.txt
//
// DIGIT 2005, Javat tanítok
// Bátfai Norbert, [email protected]
//
// A labirintus szerkezetét megadó állomány,
// szerkezete a következő:
//
// a kincsek száma
// a szörnyek száma
// a labirintus szélessége
// magassága
// fal=1 járat=0 ...
// .
// .
// .
6
3
10
10
0 0 0 1 0 1 0 1 1 1
0 0 0 0 0 0 0 0 0 0
1 0 1 0 1 0 1 0 1 0
0 0 0 0 1 0 1 0 0 0
0 1 1 0 0 0 1 1 0 1
0 0 0 0 1 0 0 0 0 0
0 1 0 0 0 1 0 1 1 0
0 0 0 1 0 1 0 1 0 0
0 1 0 0 0 0 0 0 0 1
0 0 0 0 1 0 0 0 1 1
Állományból beolvasni egy Java I/O csatorna objektumon keresztül tudunk, tehát első lépésünk ennek
létrehozása
java.io.BufferedReader szövegesCsatorna = new java.io.BufferedReader(
new java.io.FileReader(labirintusFájlNév));
a nevével megadott állomány fölött nyitunk egy karakteres állományokat Olvasó FileReader bejövő csatornát,
majd most e fölött rögtön egy BufferedReader csatornát. Ezen keresztül akarunk olvasni az állományból, mert
ettől a csatorna objektumtól lehet soronkénti olvasást (readLine() függvénye) kérni, ami számunkra most
kényelmes megoldásnak tűnik. Mert a dupla perjellel kezdődő sorok figyelmen kívül hagyása után beolvassuk
az első négy sort és a megfelelő számmá alakítjuk, majd beolvasunk magasságnyi sort és soronként letördeljük,
hogy az adott cella éppen fal vagy járat-e.
String sor = szövegesCsatorna.readLine();
while(sor.startsWith("//"))
sor = szövegesCsatorna.readLine();
kincsekSzáma = Integer.parseInt(sor);
sor = szövegesCsatorna.readLine();
szörnyekSzáma = Integer.parseInt(sor);
sor = szövegesCsatorna.readLine();
szélesség = Integer.parseInt(sor);
sor = szövegesCsatorna.readLine();
Page 149
Saját világok teremtése és Java
alapok
121 Created by XMLmind XSL-FO Converter.
magasság = Integer.parseInt(sor);
Ha a program eddig sikerrel futott, akkor tudjuk, hogy milyen széles és magas a labirintusunk, azaz mekkora
logikai tömböt kell létrehoznunk, hogy ezt az adatszerkezetet el tudjuk tárolni
szerkezet = new boolean[magasság][szélesség];
Jöhet a szövegállományban megadott labirintus szerkezet beolvasása és ez alapján a szerkezetet leíró logikai
tömb megfelelő értékeinek megadása. Amilyen magas a labirintusunk, annyi sort kell feldolgoznunk
for(int i=0; i<magasság; ++i) {
sor = szövegesCsatorna.readLine();
java.util.StringTokenizer st =
new java.util.StringTokenizer(sor);
A StringTokenizer objektum segítségével tudjuk a beolvasott sort darabjaira tördelni, mely szövegdarabokat
szóközök választanak el egymástól. A következő darabot a nextToken() metódussal lehet elkérni.
for(int i=0; i<magasság; ++i) {
sor = szövegesCsatorna.readLine();
java.util.StringTokenizer st =
new java.util.StringTokenizer(sor);
for(int j=0; j<szélesség; ++j) {
String tegla = st.nextToken();
if(Integer.parseInt(tegla) == 0)
szerkezet[i][j] = false;
else
szerkezet[i][j] = true;
A Integer osztály statikus parseInt() módszerével a beolvasott szövegdarabból számot készítünk, s majd
attól függően, hogy ez a szám 0 vagy 1, beállítjuk a labirintus szerkezetét reprezentáló tömb megfelelő elemét,
hamisra vagy igazra, azaz falra vagy járatra.
Az állományból beolvasó konstruktor csontvázát ezzel átvettük, a hús-vér konstruktort majd A tiszta OO
labirintus - Labirintus Világ című pontban folytatva adjuk meg.
1.1.5.2.3. String hasonlító feladat
Olvassuk be a labirintus szerkezetét anélkül, hogy a tégla referenciájú szövegdarabokat számmá alakítanánk!
Megoldásként használjuk a String osztály equals() módszerét:
if("0".equals(tegla))
szerkezet[i][j] = false;
else
szerkezet[i][j] = true;
Page 150
Saját világok teremtése és Java
alapok
122 Created by XMLmind XSL-FO Converter.
1.1.5.2.4. null referencia feladat
Miért szerencsésebb általában a "0".equals(tegla) utasítás, mint a tegla.equals("0")?
Mert mi történik, ha a tegla értéke éppen null? Megtudja az Olvasó, ha az alábbi osztályt kipróbálja!
public class Tégla {
public static void main(String[] args) {
String tegla = "QWERT";
if(tegla.equals("QWERT"))
System.out.println("A tegla tartalma most QWERT");
tegla = null;
if(tegla.equals("QWERT"))
System.out.println("A tegla tartalma most QWERT");
}
}
Fordítás és futtatás után
C:\...> javac Tégla.java
C:\...> java Tégla
A tegla tartalma most QWERT
Exception in thread "main" java.lang.NullPointerException
at Tégla.main(Tégla.java:12)
Olvassuk el a hibaüzeneteket
Akár a fordítás, akár a futtatás során igaz, hogy mindig érdemes és fontos a kiírt hibát gondosan
tanulmányozni. Az egyik legalapvetőbb információ, hogy a hiba hol keletkezett, mert a program
szövegének ezt a jelzett helyét megtekintve az idővel kialakuló rutinos szem már könnyen észreveheti a
hibát. Egy futási hibával megáll a programunk, mivel működése közben egy java.lang.NullPointerException
kivétel váltódik ki, mert olyan objektumra hivatkoztunk, amikor a - valójában null és nem egy vélt sztring
objektum referencia értékű - tegla-nak hívni akartuk az equals() metódusát, ami gyakorlatilag nem is létezik.
Persze most könnyű észrevenni, hogy nem létezik, hiszen a tegla = null; utasítással mi magunk töröltük, de
egy bonyolultabb programban ez nem mindig látszik, ezért érdemes a sztring literálként megadott, ezért mindig
létező „0” sztring objektumnak hívni esetünkben az equals() metódusát. Ezzel tipikusan megspórolunk egy
if(tegla != null) jellegű feltételvizsgálatot, amivel persze az ilyen típusú problémákat szintén orvosolni
lehetne:
public class Tégla {
public static void main(String[] args) {
String tegla = "QWERT";
if(tegla.equals("QWERT"))
System.out.println("A tegla tartalma most QWERT");
tegla = null;
Page 151
Saját világok teremtése és Java
alapok
123 Created by XMLmind XSL-FO Converter.
if(tegla != null)
if(tegla.equals("QWERT"))
System.out.println("A tegla tartalma most QWERT");
}
}
de láthatóan jóval hatékonyabb az alábbi módon megszervezni a kódot.
public class Tégla {
public static void main(String[] args) {
String tegla = "QWERT";
if(tegla.equals("QWERT"))
System.out.println("A tegla tartalma most QWERT");
tegla = null;
if("QWERT".equals(tegla))
System.out.println("A tegla tartalma most QWERT");
}
}
A RuntimeException típusú hibák kezeléséről 1.
A hamarosan következő Kivételkezelés című pontban látjuk majd, hogy a kivéteket hogyan kezelhetjük
programunkból, hogyan kaphatjuk el őket. Ott fogunk egy rossz példát mutatni arra a rossz gyakorlatra,
amikor például ezt a RuntimeException kivételt kezelni próbálja a programozó. (A kapott
NullPointerException kivétel osztály a RuntimeException gyermeke.) Már itt hangsúlyozzuk,
hogy az ilyen típusú hibákat a program szövegének gondosabb kifejlesztésével kell elkerülni és nem a
kivételkezelést mint tüneti kezelést alkalmazni erre.
1.1.6. Eseménykezelés
Esemény alatt azt értjük, mint a köznyelvben is: esemény az, amikor történik valami. Speciálisan, amikor a
program világában történik valami. Például megmozdítjuk az egeret, becsukjuk a program ablakát stb. Az
események, mint Javaban minden, maguk is objektumok.
Az események kezelése az adott eseménytípusnak megfelelő interfészeken keresztül történik. A programozó
azokban az osztályaiban, ahol az események keletkeznek, jelzi, hogy melyik objektum dolgozza majd fel az
eseményeket. Ennek a feldolgozó objektumnak pedig tudni kell fogadnia a megfelelő eseményobjektumokat.
Például a mobilos labirintus példánkban egy LabirintusVaszon vászon objektum uralja a mobil kijelzőjét,
amit a LabirintusMIDlet osztályban készítünk el:
labirintusVászon = new LabirintusVaszon();
// A kilépés parancs elkészítése
kilépésParancs = new javax.microedition.lcdui.Command("Kilép",
javax.microedition.lcdui.Command.EXIT, 1);
// és a labirintus vászonra helyezése
labirintusVászon.addCommand(kilépésParancs);
// az eseményeket (most kilépés parancs) itt dolgozzuk fel
labirintusVászon.set(this);
Page 152
Saját világok teremtése és Java
alapok
124 Created by XMLmind XSL-FO Converter.
A telefon Kilép gombnak megfelelő szoftbillentyűjét megnyomva a megfelelő Command objektummal
meghívódik a labirintusVászon-ba a labirintusVászon.setCommandListener(this); parancs
eseményfigyelőt beállító hívással bejegyzett objektum commandAction() eseménykezelő függvénye. Ez a this
objektum most maga a LabirintusMIDlet osztály, aminek fejét ennek megfelelően így írtuk:
public class LabirintusMIDlet extends javax.microedition.midlet.MIDlet
implements javax.microedition.lcdui.CommandListener {
azaz implementálja a CommandListener parancsokat figyelő interfészt. Ennek az interfésznek egyetlen
metódusa van, az említett commandAction(). Tehát a LabirintusMIDlet osztályban implementálnunk kell ezt
a függvényt:
/**
* A labirintus játék parancsainak (jelen esetben egy ilyen van,
* a kilépés) kezelése.
*
* @param command parancs, ami keletkezett
* @param displayable valamelyik képernyőn
*/
public void commandAction(javax.microedition.lcdui.Command parancs,
javax.microedition.lcdui.Displayable képernyő) {
if (képernyő == labirintusVászon) {
if (parancs == kilépésParancs) {
// Leállítjuk a labirintus játék szálát
labirintusVászon.játékKilép();
// Leállítjuk a programot
kijelző.setCurrent(null);
destroyApp(true);
notifyDestroyed();
}
}
}
Ha a használni kívánt eseménykezelő interfészben több metódus van, akkor sokszor kényelmetlen a függvény
fejében jelezni az interfész implementálását, mert ekkor az osztályban az interfész minden metódusának meg
kell adni az implementációját. Amivel persze éppen nem akarunk foglalkozni, azt üres testtel implementálva.
Mégis kényelmesebb az úgynevezett adapter osztályok használata, akik a megfelelő interfész minden
módszerének megadják az üres testű implementációját, mi pedig ezt az osztályt kiterjesztjük és a minket érintő
módszereit felüldefiniáljuk: így nekünk nem kell lélekölően a számos üres testű függvény implementációt
begépelnünk. Erre számos példát tudunk mutatni a kézikönyv forrásaiban, például a LabirintusJáték
osztályban a billentyűzet eseményeket dolgozzuk fel a java.awt.event.KeyAdapter adapter osztállyal:
// A hős mozgatása a KURZOR billenytűkkel, ESC kilép
addKeyListener(new java.awt.event.KeyAdapter() {
public void keyPressed(java.awt.event.KeyEvent billentyűEsemény) {
int billentyű = billentyűEsemény.getKeyCode();
if(!játékVége)
switch(billentyű) { // hős mozgatása
case java.awt.event.KeyEvent.VK_UP:
hős.lépFöl();
break;
case java.awt.event.KeyEvent.VK_DOWN:
hős.lépLe();
Page 153
Saját világok teremtése és Java
alapok
125 Created by XMLmind XSL-FO Converter.
break;
case java.awt.event.KeyEvent.VK_RIGHT:
hős.lépJobbra();
break;
case java.awt.event.KeyEvent.VK_LEFT:
hős.lépBalra();
break;
}
// Kilépés a játékból
if(billentyű == java.awt.event.KeyEvent.VK_ESCAPE)
játékKilép = true;
// A játékban történt változások a képernyőn
// is jelenjenek meg
rajzolniKell();
};
});
vagy a MandelbrotHalmazNagyító osztályunkban az egéreseményeket dolgozzuk fel a
java.awt.event.MouseAdapter adapterrel:
// Egér kattintó események feldolgozása:
addMouseListener(new java.awt.event.MouseAdapter() {
// Egérkattintással jelöljük ki a nagyítandó terület
// bal felső sarkát vagy ugyancsak egér kattintással
// vizsgáljuk egy adott pont iterációit:
public void mousePressed(java.awt.event.MouseEvent m) {
// Az egérmutató pozíciója
x = m.getX();
y = m.getY();
// Az 1. egér gombbal a nagyítandó terület kijelölését
// végezzük:
if(m.getButton() == java.awt.event.MouseEvent.BUTTON1 ) {
// A nagyítandó kijelölt terület bal felső sarka: (x,y)
// és szélessége (majd a vonszolás növeli)
mx = 0;
my = 0;
repaint();
} else {
// Nem az 1. egér gombbal az egérmutató mutatta c
// komplex számból indított iterációkat vizsgálhatjuk
MandelbrotIterációk iterációk =
new MandelbrotIterációk(
MandelbrotHalmazNagyító.this, 50);
new Thread(iterációk).start();
}
}
// Vonszolva kijelölünk egy területet...
// Ha felengedjük, akkor a kijelölt terület
// újraszámítása indul:
public void mouseReleased(java.awt.event.MouseEvent m) {
if(m.getButton() == java.awt.event.MouseEvent.BUTTON1 ) {
...
avagy ugyanitt az egérmozgásával kapcsolatos eseményeket a java.awt.event.MouseMotionAdapter
adapterrel:
// Egér mozgás események feldolgozása:
addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
// Vonszolással jelöljük ki a négyzetet:
public void mouseDragged(java.awt.event.MouseEvent m) {
Page 154
Saját világok teremtése és Java
alapok
126 Created by XMLmind XSL-FO Converter.
// A nagyítandó kijelölt terület szélessége és magassága:
mx = m.getX() - x;
my = m.getY() - y;
repaint();
}
});
Számos további példát említhetnénk még, de csak a további fő típusokra utalva: a GaltonDeszka osztályban
foglalkozunk az ablakkal kapcsolatos eseményekkel:
// Az ablak bezárásakor kilépünk a programból.
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
setVisible(false);
System.exit(0);
}
});
illetve a például az ExorTitkositoGUI osztályban nyomógombon való kattintást dolgozunk fel:
kódolButton.addActionListener(
new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent
e) {
Továbbá érdekességként említjük, hogy például a rendszer értesítési területéről érkező eseményeket dolgoz fel a
LabirintusAlkalmazás osztály. A Pontmátrix osztályban pedig egy legördülő menüből vesszük át a
felhasználói inputot.
1.1.7. Kivételkezelés
Ha a program futása közben nem azt csinálja, amit a normális működése során a programozója feltételezett,
hanem valami más ritkán bekövetkezőt, nem vártat, szokatlant, hibásat - egyszóval valami kivételeset -, akkor
azt mondjuk, hogy kivétel keletkezett. A kivételek, mint Javaban minden, maguk is objektumok. A program
szövegének azt a részét, amit a programozó figyelni akar, hogy a végrehajtása közben keletkezik-e kivétel, egy
try kulcsszóval bevezetett blokkba kell foglalni. Ha bekövetkezik egy kivétel, akkor a program végrehajtása a
tartalmazó try blokknak megfelelő catch kulcsszóval bevezetett blokkban folytatódik.
Egyszerű példaként folytassuk a Vezérlési szerkezetek pontban megkezdett, állományból olvasó konstruktor
írását! A labirintus szerkezetét leíró állomány a programon kívüli erőforrás, ezért is könnyű vele a játékot
variálni, mert nem kell a programhoz nyúlnunk, ha a labirintusnak más szerkezetet szeretnénk. Viszont ez a
programon kívüliség számos probléma (vagy jelen terminológiánkban: kivétel) forrása lehet. Gondoljunk arra
például, hogy hogyan viselkedjen a program akkor, ha nincs meg ez az állomány! Ha a labirintus felépítése
során valami gondunk támad, azaz a konstruktor futása során kivétel keletkezik, akkor az ilyen nagyobb
problémákra való válaszadást a hívóra, azaz arra bízzuk, aki létre akarta hozni ily módon a labirintust. Ezért a
konstruktor fejében jelezzük, hogy megszakíthatja futását és egy RosszLabirintusKivétel objektumot dobhat
a hívónak, amely objektumba becsomagoltuk ennek a valamilyen nagyobb hibának a leírását.
public Labirintus(String labirintusFájlNév) throws RosszLabirintusKivétel {
}
Page 155
Saját világok teremtése és Java
alapok
127 Created by XMLmind XSL-FO Converter.
Vegyük most szemügyre a br referenciájú, BufferedReader osztálybeli objektum példányosításának pontos
forráskódrészletét!
java.io.BufferedReader szövegesCsatorna = null;
try {
szövegesCsatorna = new java.io.BufferedReader(
new java.io.FileReader(labirintusFájlNév));
Ez az objektum a labirintusunkat leíró szöveges állományra nyitott csatorna objektum, amin keresztül
beolvassuk a labirintus szerkezetének sorait a BufferedReader osztálybeli objektum readLine() metódusával
a külső for ciklusban. Itt a program feltételezett normális működésétől való eltérés, azaz kivétel például az
lehet, hogy a program nyitni akarja a szöveges állományra a csatorna objektumot, de a szöveges állomány nem
létezik, mert például nem megfelelő helyre másoljuk.
De más kivételekbe is beleszaladhat a program, például a beolvasott első 4 adat nem szám, hanem egy egyszerű
elírás miatt példának okáért egy betű
try {
kincsekSzáma = Integer.parseInt(sor);
sor = szövegesCsatorna.readLine();
szörnyekSzáma = Integer.parseInt(sor);
sor = szövegesCsatorna.readLine();
szélesség = Integer.parseInt(sor);
sor = szövegesCsatorna.readLine();
magasság = Integer.parseInt(sor);
szerkezet = new boolean[magasság][szélesség];
} catch(java.lang.NumberFormatException e) {
throw new RosszLabirintusKivétel("Hibás a kincsek, szörnyek száma, "
+"szélesség, magasság megadási rész.");
}
Ha itt a try blokkba foglalt kódrészletnek bármely pontján kivétel keletkezik, akkor a végrehajtás a catch
blokknál folytatódik, s ha ez a kivétel a szövegdarabok számmá konvertálása kapcsán keletkezett (amit az mutat
meg, hogy a kivétel objektum a NumberFormatException osztálybeli) akkor a vezérlés ebben a catch
blokkban folytatódik. Ha a kincsek vagy szörnyek számával lenne a baj, akkor azt itt még értelmesen is tudnánk
kezelni, mert például azt mondanánk, hogy legyen 3 kincs, ha a szöveges állományban a szám helyett mondjuk
tévedésből egy „a” betűt adtunk meg. Viszont a labirintus szélességénél és magasságánál már nem tudunk ilyen
jó hibajavítást javasolni, mert ha itt mondunk egy számot hasraütésre, az biztos nem lesz éppen annyi, ahány
oszlop és sor adat szerepel lejjebb. Ezért érdekes megoldást alkalmazunk kezelésként: nem kezelünk, hanem
tovább dobunk egy kivételt.
throw new RosszLabirintusKivétel("Hibás a kincsek, szörnyek száma,
szélesség, magasság megadási rész.");
ne feledjük, hogy try-catch blokkunk egy másik ilyen blokkba van ágyazva
Page 156
Saját világok teremtése és Java
alapok
128 Created by XMLmind XSL-FO Converter.
try {
szövegesCsatorna = new java.io.BufferedReader(
new java.io.FileReader(labirintusFájlNév));
String sor = szövegesCsatorna.readLine();
while(sor.startsWith("//"))
sor = szövegesCsatorna.readLine();
try {
kincsekSzáma = Integer.parseInt(sor);
sor = szövegesCsatorna.readLine();
szörnyekSzáma = Integer.parseInt(sor);
sor = szövegesCsatorna.readLine();
szélesség = Integer.parseInt(sor);
sor = szövegesCsatorna.readLine();
magasság = Integer.parseInt(sor);
szerkezet = new boolean[magasság][szélesség];
} catch(java.lang.NumberFormatException e) {
throw new RosszLabirintusKivétel("Hibás a kincsek, szörnyek száma,
szélesség, magasság megadási rész.");
}
// ITT VAN A SOROK OSZLOPOK FELDOLGOZÁSA
} catch(java.io.FileNotFoundException e1) {
throw new RosszLabirintusKivétel("Nincs meg a fájl: " + e1);
} catch(java.io.IOException e2) {
throw new RosszLabirintusKivétel("IO kivétel történt: "+e2);
} catch(java.util.NoSuchElementException e3) {
throw new RosszLabirintusKivétel("Nem jó a labirintus szerkezete: "+e3);
} finally {
if(szövegesCsatorna != null) {
try{
szövegesCsatorna.close();
} catch(Exception e) {}
}
}
A befoglaló try blokk valamely catch ága elkapja a dobott RosszLabirintusKivétel kivételünket? Nem,
mert nincs ilyen ága! Ezért ez a kivétel tovább dobódik a hívónak. Nézzük mit csinál ezzel a továbbdobott
kivétellel a LabirintusVilág példaprogram mint hívó. A LabirintusVilág konstruktora készíti el a
labirintust, de a RosszLabirintusKivétel kivételt ő sem kezeli, csak jelzi, hogy ha ilyen lenne, akkor ő dobná
tovább a hívónak.
public LabirintusVilág(String labirintusFájlNév) throws RosszLabirintusKivétel {
Page 157
Saját világok teremtése és Java
alapok
129 Created by XMLmind XSL-FO Converter.
// A labirintus elkészítése állományból
labirintus = new Labirintus(labirintusFájlNév);
A hívó main()-jében van a megfelelő kezelő:
public static void main(String[] args) {
try {
new LabirintusVilág(args[0]);
} catch(RosszLabirintusKivétel rosszLabirintusKivétel) {
System.out.println(rosszLabirintusKivétel);
}
}
s itt találjuk a kezelést is: kiírjuk, hogy mi volt a probléma a labirintus elkészítése során, majd a program leáll.
Milyen kivételek váltódhatnak itt ki? Ugye nem az elvárt normális működés, ha a labirintust leíró szöveges
állományunk nincs meg, ekkor egy java.io.FileNotFoundException kivétel objektum keletkezik még az
elején, amikor a csatornát ki akarjuk nyitni az állomány felett. Ha sikerül megnyitni az állomány felett csatornát,
a sorok olvasása vagy a csatorna lezárása során keletkezhet java.io.IOException kivétel. Ha mondjuk
elrontjuk ezt a szöveges állományt, az egyik sorában letöröljük az utolsó számot, akkor amikor éppen ezt a
hiányzó számot kellene beolvasni, azaz a
String tegla = st.nextToken();
utasítás végrehajtásakor egy java.util.NoSuchElementException kivétel fog kiváltódni. Ha nem kitörölünk,
hanem csak mondjuk átírjuk egy „a” betűre, akkor (lást az imént végigjátszott esetet) egy
java.lang.NumberFormatException kivétel váltódik ki, amikor számmá próbáljuk alakítani a már beolvasott
szövegdarabot.
Nézzünk egy olyan részt, ahol valódi kezelést alkalmaztunk, a sorok és oszlopok feldolgozásánál:
for(int i=0; i<magasság; ++i) {
sor = szövegesCsatorna.readLine();
java.util.StringTokenizer st =
new java.util.StringTokenizer(sor);
for(int j=0; j<szélesség; ++j) {
String tegla = st.nextToken();
try {
if(Integer.parseInt(tegla) == 0)
szerkezet[i][j] = false;
else
szerkezet[i][j] = true;
} catch(java.lang.NumberFormatException e) {
System.out.println(i+". sor "+j+". oszlop "+e);
Page 158
Saját világok teremtése és Java
alapok
130 Created by XMLmind XSL-FO Converter.
szerkezet[i][j] = false;
}
}
}
Itt azt is meg tudjuk mondani, hogy a labirintust leíró állomány melyik része volt hibás és menet közben javítjuk
is, azaz azt tételezzük fel, hogy ott 0 áll, ezért a kezelő blokkban hamisra állítjuk a tömb megfelelő elemét.
Továbbá egy ilyen hiba miatt nem szakad meg a állomány további feldolgozása!
2.2. példa - A RuntimeException típusú hibák kezeléséről 2.
A korábbi, A RuntimeException típusú hibák kezeléséről 1. című pontban említett elkerülendő és rossz
gyakorlatot mutatjuk be itt. Úgy módosítjuk a Tégla osztályt, hogy elkapjuk a NullPointerException
kivételt:
public class Tégla {
public static void main(String[] args) {
String tegla = "QWERT";
if(tegla.equals("QWERT"))
System.out.println("A tegla tartalma most QWERT");
tegla = null;
try {
if(tegla.equals("QWERT"))
System.out.println("A tegla tartalma most QWERT");
} catch(RuntimeException e) {
e.printStackTrace(System.out);
System.out.flush();
}
System.out.println("A végén vagyok.");
}
}
Láthatóan programunk nem állt le, a kivételt kezeltük, de mégis: ennek a gyakorlatnak nincs értelme! Mert a
példaként tárgyalt hiba, hogy egy (már vagy még) nem létező objektum metódusát akarjuk meghívni, szinte
bárhol előfordulhat a programunk szövegében, így melyik részét figyelnénk try-catch blokkjainkkal? Az ilyen
típusú hibákat a program szövegének gondos kifejlesztésével, megírásával kerülhetjük el értelmesen!
1.1.8. Párhuzamos végrehajtás
Javaban a párhuzamos végrehajtás szál objektumok formájában valósul meg. A szálak az alapvető, azaz a
java.lang csomagbeli Thread osztály leszármazottai.
1.1.8.1. Thread objektumok
A párhuzamosan végezhető, végezendő tevékenységeket tehát olyan osztályokban implementáljuk, amik
kiterjesztik a java.lang.Thread osztályt. Nincs más dolgunk, mint a párhuzamosan végrehajtatni óhajtott
tevékenységet az osztály run() módszerében implementálni. Viszont mivel Javaban az öröklődés egyszeres,
azaz csak egy osztályt szerepeltethetünk az extends kulcsszó után, így a
Page 159
Saját világok teremtése és Java
alapok
131 Created by XMLmind XSL-FO Converter.
public class Párhuzamos extends Thread {
osztályt például már nem tudnánk egy másik osztályból is származtatni. Ebben segít a Runnable interfész.
1.1.8.2. Runnable objektumok
A Runnable interfésznek egyetlen metódusa a run() metódus. A Runnable interfészt implementáló osztályokat
szoktuk Runnable objektumoknak is nevezni. Az osztályunkat származtatjuk onnan, ahonnan származtatnunk
kell, jelezzük, hogy osztályunkban implementáljuk a szóban forgó interfészt és osztályunk run() módszerében
implementáljuk a párhuzamosan végrehajtandó tevékenységet. Például Sejtautomata osztályunk örököl a
java.awt.Frame osztályból, de a sejtautomata szimulációt egy párhuzamos programszálában végzi.
public class Sejtautomata extends java.awt.Frame implements Runnable {
Ehhez, ennél a Runnable interfészt kiterjesztő módszernél is készítünk szálat, aminek paramétereként adjuk
meg a Runnable objektum referenciáját, például az említett osztály konstruktorában:
new Thread(this).start();
ahol a start() metódus meghívásával rögtön indítottuk is az elkészített szálat. Ez a run(), a párhuzamosan
végrehajtandó kódot tartalmazó módszer implicit meghívását jelenti:
/** A sejttér időbeli fejlődése. */
public void run() {
while(true) {
try {
Thread.sleep(várakozás);
} catch (InterruptedException e) {}
időFejlődés();
repaint();
}
}
A kézikönyv példái között számos további esetben láthatjuk és tanulmányozhatjuk a Runnable interfész
használatát. Csak néhányat kiragadva, például a LabirintusKiszolgáló osztályból:
public class LabirintusKiszolgáló
extends javattanitok.labirintus.TöbbHősösLabirintus
implements LabirintusOperations, Runnable {
vagy a LabirintusVaszon osztályt megnézve:
public class LabirintusVaszon extends javax.microedition.lcdui.game.GameCanvas
implements Runnable {
Page 160
Saját világok teremtése és Java
alapok
132 Created by XMLmind XSL-FO Converter.
1.1.9. Interfészek
Már számos interfésszel találkoztunk eddig is a gyakorlatban, az iménti Runnable interfészt megelőzően az
eseménykezelési részben például, ahol az Eseménykezelés című pontban megismerkedtünk a CommandListener
interfész használatával. Az API dokumentációt fellapozva láthatjuk, hogy ez az interfész egyetlen metódus
specifikációját tartalmazza:
void commandAction(javax.microedition.lcdui.Command c,
javax.microedition.lcdui.Displayable d);
A CommandListener interfészt implementáló osztály feladata, hogy ezt a függvényt implementációval lássa el,
erre láthattunk példát a A LabirintusMIDlet osztály című pontba foglalt LabirintusMIDlet osztály kódjában.
Az interfészek az osztályokhoz hasonlóak, lehetnek tulajonságaik, de a függvényeiket maguk csak deklarálják,
testtel nem látják el őket. Nézzük meg például a JDK könyvtárában található src.zip állományban - ahol ugye
megtaláljuk a Java SE OO világ összes osztályának forráskódját - az src.zip/java/lang/Runnable.java
forrásban a Runnable interfészt! A Runnable interfészt számos saját példánkban kiterjesztjük, ilyenek például
az önálló időfejlődéssel ellátott labirintus példáink vagy a grafikus felületet és egyben számítást vagy
szimulációt tartalmazó példáink is, mint a Mandelbrot halmaz vagy a Galton deszka kísérlet vagy éppen a
Sejtautomata szimuláció programja.
Az interfészek tipikus felhasználási területe az előző pontban említett többszörös öröklődés hiányának
orvoslása. A kezdő fejlesztő legtöbbször az eseménykezelés és a párhuzamosság implementálása során
találkozik velük, de idővel saját programjait is megtanulja általánosabbá tenni az interfészek használatával, mert
ahová például egy formális paraméterként interfészt írunk, oda aktuálisan bármilyen objektumot átadhatunk,
aminek példányosító osztálya ezt az interfészt implementálja.
1.1.10. Csomagok
Ha OO világunk leírása során fejlesztett osztályaink száma megszaporodik, akkor ezeket az osztályokat érdemes
csoportokba szervezni. Így tettünk mi is, amikor például a labirintus absztahálásával kapcsolatos osztályainkat a
javattanitok.labirintus csomagba szerveztük. A csomagokkal kapcsolatos alapismereteket a A Java
szófajok című fejezetben, a package ismertetésénél tárgyaltuk.
1.2. A Java nyelv használata
A programozás megtanulásához programok írásán keresztül vezet az út, a konkrét programozási nyelvhez -
esetünkben a Javahoz - kapcsolódó gyakorlati tapasztalatok ebben a tanulási folyamatban nélkülözhetetlenek.
Ezeknek a tapasztalatoknak a megszerzését akartuk segíteni a könyvhöz készített esettanulmányokkal és a
szereplő további, mindenféle - általunk érdekesnek ítélt - példákkal. Ezeken túl még azt tudjuk javasolni az
érdeklődő Olvasónak, hogy folyamatosan tanulmányozza a fejlesztői portálok cikkeit, például Java tekintetében
a [SUN JAVA CIKKEK] portál cikkeit, vagy például mobil programozás tekintetében a [FORUM NOKIA
CIKKEK] portál fejlesztői cikkeit.
1.2.1. Bepillantás a GUI programozásba
Grafikus felület építésére a Java SE keretein belül az AWT, java.awt.* és a Swing javax.swing.* csomagok
szolgálnak. Az előbbi a kezdetek óta része a Javanak, az utóbbi a Java 1.3-tól része a JDK-nak, azaz a Java 2
platform 1.3 verziójától.
A Java ME, MIDP keretein belül sem az AWT, sem a Swing csomag nem elérhető, a mobilokon a grafikus
felület felépítésére a javax.microedition.lcdui.* csomag használandó. Ennek a csomagnak a bevezetését a
Java a mobiltelefonokban: MIDlet objektumok - Labirintus MIDlet című pont alatt tárgyaljuk.
Az AWT programozása egyszerűbb, de az AWT-vel felépített felület kinézete platformfüggő. A Swing
programozása - a párhuzamosság, azaz a szálkezelés - miatt némileg bonyolultabb. A kézikönyvben számos
AWT-s felületű példát találunk, ilyenek például a Galton deszka kísérlet, a Sejtautomata szimuláció programja
Page 161
Saját világok teremtése és Java
alapok
133 Created by XMLmind XSL-FO Converter.
vagy például a Mandelbrot halmaz programja című pontokban fejlesztett osztályok. Ebben a pontban Swinges
példákat fogunk fejleszteni.
A Java felületépítés erőssége, hogy nem kell pixel pontossággal megterveznünk a grafikus felület kinézetét -
például egy nyomógomb komponens méretét, helyzetét -, hanem mindenféle elhelyezési stratégiát követhetünk,
amik helyettünk meghatározzák a komponensek pontos elhelyezést. Kellemes ez, ha arra gondolunk, hogy
programunk ablakának természetes tulajdonsága az átméretezhetősége. Mi mégis egy olyan példával indítunk
most, ahol nem használunk elhelyezési stratégiát, hanem magunk írjuk elő a használt komponensek méretét és
helyzetét. Ezt azért is tehetjük meg bátran, mert az integrált fejlesztői környezetek, mint például az általunk
használni javasolt NetBeans is, hatékony támogatást adnak a felület egérrel történő összehúzogatására,
miközben eleve azt a felületet látjuk, amit építünk éppen. Ezért térünk most vissza az ősi módszerhez:
négyzethálós, matekfüzet lapján tervezzük meg a felületet. Egy ilyen skiccet mutatunk a következő ábrán, a
korábban, a Kizáró vagyos titkosítás című pontban tárgyalt exor titkosítást megvalósító programunkat látjuk
most el grafikus felülettel.
public class ExorTitkositoGUI extends javax.swing.JFrame {
public ExorTitkositoGUI() {
// Az ablak adatai, fejléce:
super("Javat tanitok példa");
// az ablakot ne lehessen átméretezni, mert
setResizable(false);
// nem használunk elhelyezési stratégiát (hanem majd mi mondjuk meg,
// melyik komponens melyik pozíción és mekkora legyen).
getContentPane().setLayout(null);
// az ablak mérete
setBounds(100, 50, 210, 250);
// az ablak szokásos bezár gombjára kilép a program:
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
// a titkosító kulcs szövegmezője
final javax.swing.JTextField kulcsTextField
= new javax.swing.JTextField();
kulcsTextField.setText("kulcs");
kulcsTextField.setBounds(5, 5, 130, 20);
kulcsTextField.setToolTipText("Titkosító kulcs");
getContentPane().add(kulcsTextField);
Page 162
Saját világok teremtése és Java
alapok
134 Created by XMLmind XSL-FO Converter.
// a tiszta szöveg doboza
final javax.swing.JTextArea tisztaTextArea
= new javax.swing.JTextArea();
javax.swing.JScrollPane tisztaScrollPane
= new javax.swing.JScrollPane();
tisztaScrollPane.setViewportView(tisztaTextArea);
tisztaScrollPane.setBounds(5, 30, 190, 80);
tisztaScrollPane.setToolTipText("A kódolandó/dekódolandó szöveget írd ide!");
getContentPane().add(tisztaScrollPane);
// a titkos szöveg doboza
final javax.swing.JTextArea titkosTextArea
= new javax.swing.JTextArea();
javax.swing.JScrollPane titkosScrollPane
= new javax.swing.JScrollPane();
titkosScrollPane.setViewportView(titkosTextArea);
titkosScrollPane.setBounds(5, 125, 190, 80);
titkosScrollPane.setToolTipText("Itt kapod az eredményt.");
getContentPane().add(titkosScrollPane);
// a kódoló/dekódoló gomb
javax.swing.JButton kódolButton
= new javax.swing.JButton();
kódolButton.setText("K/D");
kódolButton.setBounds(140, 5, 55, 20);
kódolButton.setToolTipText("Kódolás/Dekódolás");
kódolButton.addActionListener(
new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent
e) {
byte [] kulcs = kulcsTextField.getText().getBytes();
byte [] buffer = tisztaTextArea.getText().getBytes();
int kulcsIndex = 0;
for(int i=0; i<buffer.length; ++i) {
buffer[i] = (byte)(buffer[i] ^ kulcs[kulcsIndex]);
kulcsIndex = (kulcsIndex+1) % kulcs.length;
}
System.out.println(new String(buffer));
titkosTextArea.setText(new String(buffer));
}
});
getContentPane().add(kódolButton);
// Lássuk!
setVisible(true);
}
public static void main(String [] args) {
new ExorTitkositoGUI();
}
}
Az exor titkosítós, immár grafikus köntösbe öltöztetett példánk kódol.
Page 163
Saját világok teremtése és Java
alapok
135 Created by XMLmind XSL-FO Converter.
A kódolt szöveget visszamásoltuk a tiszta szöveg dobozába, majd újra kódolva dekódoltuk.
A Swinges programozási tapasztalatainkat a Genomi, aminosav vagy akár tetszőleges szekvenciák
összehasonlítása című pont tanulmányozásával növelhetjük, ahol egy menüsorral is felszerelt felületű programot
írunk. Továbbá rámutatunk a Swing programozásban leginkább szem előtt tartandó programozási gyakorlatra,
miszerint ne csináltassunk időigényes dolgokat az eseménykezelőkben, mert ez a felület lefagyását okozhatja.
Mivel az eseménykezelést és a megjelenítést ugyanaz a programszál végzi, így ha az eseménykezelőt egy
hosszadalmas számítással blokkoljuk, akkor ezzel egyben a felület megjelenítését is blokkoljuk! (Amit tipikusan
a program lefagyásaként szoktunk értékelni.)
1.2.2. Bepillantás a hálózati programozásba
Javaban hálózati programozás tekintetében a TCP/IP jelölte absztrakciós szinten és a felett URL osztályokkal,
Java szervletekkel, a Java távoli metódushívásával vagy CORBA szinten dolgozhatunk.
A TCP/IP-nek megfelelő osztályok absztrakciós szintje alá Javaban nem hatolhatunk, csak érdekességként
jegyezzük meg, hogy ennek megfelelően például Javaban nem tudunk ICMP ping jellegű programot írni. A ping
programot szoktuk használni, ha arra vagyunk kíváncsiak, hogy egy távoli gép elérhető-e egyáltalán. Elérhető
például a niobe?
C:\Documents and Settings\norbi> ping niobe
niobe [192.168.1.1] pingelése 32 bájt méretű adatokkal:
Válasz 192.168.1.1: bájt=32 idő<10 ezredmp. TTL=64
Válasz 192.168.1.1: bájt=32 idő<10 ezredmp. TTL=64
Page 164
Saját világok teremtése és Java
alapok
136 Created by XMLmind XSL-FO Converter.
Válasz 192.168.1.1: bájt=32 idő<10 ezredmp. TTL=64
Válasz 192.168.1.1: bájt=32 idő<10 ezredmp. TTL=64
192.168.1.1 ping-statisztikája:
Csomagok: küldött = 4, fogadott = 4, elveszett = 0 (0% veszteség),
Oda-vissza út ideje közelítőlegesen, milliszekundumban:
minimum = 0ms, maximum = 0ms, átlag = 0ms
A ping jellegű programok és általában a nyers socketek használata tekintetében a [PROGRAMOZÓ
PÁTERNOSZTER JEGYZET] jegyzetet ajánljuk az érdeklődő Olvasó figyelmébe, ahol e mélység felett, a
socket interfész absztrakciós szintjén nemcsak TCP-s, hanem UDP-s Java példákat is találhatunk.
Ugyancsak érdekességként jegyezzük meg, hogy az 1.4 Java változatban megjelenő java.nio csomag
felhasználásával már Javaban is írhatunk nem blokkolódó, multiplexelt szervereket! E téma kapcsán ugyancsak
a [PROGRAMOZÓ PÁTERNOSZTER JEGYZET] jegyzetet ajánljuk az érdeklődő Olvasók figyelmébe.
1.2.2.1. A kézikönyv hálózati programjai
Az első Java tapasztalatok című pontban egy egyszerű, a socket interfész absztrakciós szintjén üzemelő TCP-s
kliens-szerver modellt mutattunk be. A Java és a C Sharp összehasonlítása című pontban ugyanez a példánk
már többszálú változatban jelenik meg. Ugyanezen az absztrakciós szinten működik egy labirintusos
esettanulmányunk, illetve egyre magasabb szintekre épülnek az alábbi labirintusos példák.
• Szerveroldali Java
a. java.net csomag
b. Java Servlet
c. Java RMI
d. Java IDL
• CORBA - elosztott programozás heterogén OO környezetben
1.2.3. Esettanulmány: egy chat program
A következő rész labirintusos esettanulmányainak technikai bevezetéseként készítsünk ebben a pontban egy
csevegő (chat) programot. Pontosabban egy TCP/IP és egy CORBA szinten üzemelő változatot. Vegyük majd
észre, hogy a CORBA implementáció mennyivel kevesebb és egyszerűbb programozási munkát igényel a
fejlesztőtől!
Figyelmeztetés a Javaban kezdő Olvasóknak
A következő Java nyelvű példák bevezetők a labirintusos esettanulmányokhoz.
Ezen példák forrásait csupán fussa át a kezdő Olvasó, hogy szeme szokja a Java nyelvet, vagy éppen a
CORBA architektúrát. Ha eközben a források megértésében bármi zavar támadna - és a teljesen
kezdőknél nyilvánvalóan támadni fog - akkor most bátran hagyja ki őket, s ha majd feldolgozta a Saját
világok teremtése és Java alapok című fejezetet, az után, tehát a labirintusos esettanulmányok előtt
térjen vissza ide és írja meg (próbálja ki) ezeket a programokat.
1.2.3.1. TCP/IP alapú csevegő
A Java és a C Sharp összehasonlítása című pontban feldolgozott Szerver osztályt fejlesztjük itt tovább. Arra
kell figyelnünk, hogy az említett példa párhuzamos szálait együttesen el kell tudni érnünk, mert a csevegő
lényege, hogy az egyik klienstől érkező üzenetet továbbítjuk az összes éppen csatlakoztatott klienshez. Tehát a
kommunikáció immár nem lehet a Kiszolgáló szál objektum és a kliens csak kettejüket érintő magánügye.
A csatlakozott csevegőket a csevegők List osztálybeli lista objektumban tartjuk nyilván. A szerver
működésének szervezésében részben követjük a [JAVA PÉLDA WEBSZERVER] cikkben is olvasható ötletet,
Page 165
Saját világok teremtése és Java
alapok
137 Created by XMLmind XSL-FO Converter.
miszerint a szerver indulásakor előre több kiszolgáló szálat készítünk, amiket azonnal el is altatunk. Ha egy
kliens jelentkezik, akkor a vele való kommunikációhoz felébresztünk egy ilyen előre elkészített és elaltatott
szálat. A hivatkozott cikkel szemben viszont, ha esetünkben elfogynak a hadra fogható szálak, akkor nem igény
szerint készítünk újakat, hanem egy rövid üzenetben közöljük a klienssel, hogy a csevegő szoba tele van, azaz
nem tesszük számára lehetővé a jelen pillanatbeli csevegésbe való bekapcsolódást.
Egészen pontosan két listát is üzemben tartunk. A csevegők listában az éppen dolgozó hálózati szálakat, azaz
az éppen csevegést bonyolító szálakat tartjuk. Míg a nemCsevegők listában az éppen nem dolgozó, azaz alvó
szálakat tartjuk.
/** Itt tartjuk az éppen csevegést lebonyolító szálakat. */
private java.util.List<Kiszolgáló> csevegők;
/** Itt tartjuk az éppen csevegést nem bonyolító szálakat. */
private java.util.List<Kiszolgáló> nemCsevegők;
Kezdetben minden elkészített és alvó szál ebben a listában kap helyet.
nemCsevegők = new java.util.ArrayList<Kiszolgáló>();
csevegők = new java.util.ArrayList<Kiszolgáló>();
// Csevegő szálak elkészítése
for(int i=0; i<MAX_CSEVEGŐ; ++i)
nemCsevegők.add(new Kiszolgáló(this));
A listák kezelését pedig az alábbi három, objektum szinten szinkronizált metódusra korlátozzuk:
/**
* Egy szálat átteszünk a nem csevegők közül a csevegőkbe.
* Az alábbi három, az adott szerverhez tartozó nem csevegőket
* a csevegőket kezelő módszer közül mindig csak egy futhat, ezt
* az objektum szintű szinkronizált módszerek használatával
* biztosítottuk.
*/
public synchronized Kiszolgáló beszáll() {
if(!nemCsevegők.isEmpty()) {
Kiszolgáló kiszolgáló = nemCsevegők.remove(0);
csevegők.add(kiszolgáló);
return kiszolgáló;
}
return null;
}
/**
* Egy szál aktuális működése végének leadminisztrálása.
*
* @param csevegő szál, aki éppen befejezte működését.
*/
public synchronized void kiszáll(Kiszolgáló csevegő) {
csevegők.remove(csevegő);
nemCsevegők.add(csevegő);
}
/**
* Üzenet küldése a csevegő klienseknek.
*
* @param üzenet amit minden kliensnek el kell küldeni.
*/
public synchronized void mindenkihez(String üzenet) {
for(Kiszolgáló csevegő: csevegők)
csevegő.üzenet(üzenet);
Page 166
Saját világok teremtése és Java
alapok
138 Created by XMLmind XSL-FO Converter.
}
Ezzel biztosítjuk, hogy adott CsevegőSzerver objektum e három metódusa közül egyszerre csak egy fusson, s
így ne zavarodjon össze a csevegő és a nem csevegő szálak adminisztrálása!
1.2.3.1.1. A CsevegőSzerver és a Kiszolgáló osztály
/*
* CsevegőSzerver.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* Egy csevegőt kiszolgáló szál.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
*/
class Kiszolgáló implements Runnable {
/** A csevegő szerver, aki ezt a kiszolgáló szálat készítette és
* használja. */
CsevegőSzerver szerver;
/** A kiszolgálást ezen a TCP/IP kapcsolatot absztraháló
* objektumon kersztül végezzük éppen. */
java.net.Socket socket;
/** A kapcsolat feletti kimenő csatorna (látnia kell a többi
* kiszolgálást végző szálnak is) */
java.io.PrintWriter kimenőCsatorna;
/** Dolgozik éppen vagy sem az objektum? */
boolean kiszolgál = false;
/**
* A {@code Kiszolgáló} objektumot felépítő konstruktor.
*
* @param szerver a csevegő szálakat összefogó szerver.
*/
public Kiszolgáló(CsevegőSzerver szerver) {
this.szerver = szerver;
// Készítjük a szálat és indítjuk, az ennek
// megfelelő run() módszerben pedig azonnal
// altatjuk majd az indított szálat.
new Thread(this).start();
}
/**
* A szál kiszolgálását indító függvény.
*
* @param socket kapcsolat a TCP/IP-s klienssel.
*/
public synchronized void kiszolgál(java.net.Socket socket) {
this.socket = socket;
kiszolgál = true;
// A run()-ban alvó szálat ébresztjük, az ott a
// wait()-nál várakozó végrehajtási szál elindul.
notify();
}
/**
* Üzenet a kliensnek.
*
* @param üzenet amit el köldünk a kliensnek.
*/
public void üzenet(String üzenet) {
kimenőCsatorna.println(üzenet);
kimenőCsatorna.flush();
}
Page 167
Saját világok teremtése és Java
alapok
139 Created by XMLmind XSL-FO Converter.
/**
* A kliensek kiszolgálását végző szál.
*/
public synchronized void run() {
for(;;) {
// Tétlenül várakozik addig, amig nincs kliens
while(!kiszolgál)
try{
// a várakozásból a notify() hívás ébreszti majd fel.
wait();
} catch(InterruptedException e) {}
// Kimenő és bejövő csatorna objektumokat elkészítjük.
try {
java.io.BufferedReader bejövőCsatorna =
new java.io.BufferedReader(
new java.io.InputStreamReader(socket.getInputStream()));
kimenőCsatorna =
new java.io.PrintWriter(socket.getOutputStream());
// Addig olvasunk, amig ki nem lép a kliens
// a vege parancs begépelésével.
String sor = bejövőCsatorna.readLine();
do {
if("vege".equals(sor))
break;
// Visszahívjuk a szervert, hogy a klienstől
// beolvasott üzenetet eljutassa minden csevegőhöz:
szerver.mindenkihez(sor);
} while((sor = bejövőCsatorna.readLine()) != null);
} catch(java.io.IOException ioE) {
ioE.printStackTrace();
} finally {
try{
socket.close();
szerver.kiszáll(this);
kiszolgál = false;
} catch(java.io.IOException ioE) {}
}
}
}
}
/**
* A csevegő szerver.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
*/
public class CsevegőSzerver {
/** Maximum hány csevegő fér be. */
public static final int MAX_CSEVEGŐ = 20;
/** Itt tartjuk az éppen csevegést lebonyolító szálakat. */
private java.util.List<Kiszolgáló> csevegők;
/** Itt tartjuk az éppen csevegést nem bonyolító szálakat. */
private java.util.List<Kiszolgáló> nemCsevegők;
/** A <code>CsevegőSzerver</code> objektumot felépítő konstruktor. */
public CsevegőSzerver() {
nemCsevegők = new java.util.ArrayList<Kiszolgáló>();
csevegők = new java.util.ArrayList<Kiszolgáló>();
// Csevegő szálak elkészítése
for(int i=0; i<MAX_CSEVEGŐ; ++i)
nemCsevegők.add(new Kiszolgáló(this));
// Szerver indítása
try {
java.net.ServerSocket serverSocket =
new java.net.ServerSocket(2006);
while(true) {
Page 168
Saját világok teremtése és Java
alapok
140 Created by XMLmind XSL-FO Converter.
java.net.Socket socket = serverSocket.accept();
Kiszolgáló szál = beszáll();
if(szál == null) {
// Ha betelt a csevegő szoba, akkor egy
// rövid tájékoztató üzenet a kliensnek:
java.io.PrintWriter kimenőCsatorna =
new java.io.PrintWriter(socket.getOutputStream());
kimenőCsatorna.println("A csevegő szoba tele van!");
socket.close();
} else // Ha van szabad csevegő szál, akkor
// azzal elkezdődik a csevegő kliens kiszolgálása.
szál.kiszolgál(socket);
}
} catch(java.io.IOException ioE) {
ioE.printStackTrace();
}
}
/**
* Egy szálat átteszünk a nem csevegők közül a csevegőkbe.
* Az alábbi három, az adott szerverhez tartozó nem csevegőket
* a csevegőket kezelő módszer közül mindig csak egy futhat, ezt
* az objektum szintű szinkronizált módszerek használatával
* biztosítottuk.
*/
public synchronized Kiszolgáló beszáll() {
if(!nemCsevegők.isEmpty()) {
Kiszolgáló kiszolgáló = nemCsevegők.remove(0);
csevegők.add(kiszolgáló);
return kiszolgáló;
}
return null;
}
/**
* Egy szál aktuális működése végének leadminisztrálása.
*
* @param csevegő szál, aki éppen befejezte működését.
*/
public synchronized void kiszáll(Kiszolgáló csevegő) {
csevegők.remove(csevegő);
nemCsevegők.add(csevegő);
}
/**
* Üzenet küldése a csevegő klienseknek.
*
* @param üzenet amit minden kliensnek el kell küldeni.
*/
public synchronized void mindenkihez(String üzenet) {
for(Kiszolgáló csevegő: csevegők)
csevegő.üzenet(üzenet);
}
/** A csevegő szerver elindítása. */
public static void main(String [] args) {
new CsevegőSzerver();
}
}
1.2.3.1.2. A csevegő kipróbálása
Az imént ismertetett Kiszolgáló és CsevegőSzerver osztályok forráskódját a CsevegőSzerver.java
állományba helyezése után fordítunk, majd futtatunk:
C:\Documents and Settings\norbi> javac CsevegőSzerver.java
C:\Documents and Settings\norbi> java CsevegőSzerver
Page 169
Saját világok teremtése és Java
alapok
141 Created by XMLmind XSL-FO Converter.
Egy másik ablakból a telnet localhost 2006 parancs kiadásával csatlakozunk a szerverhez:
C:\Documents and Settings\norbi> telnet localhost 2006
egyik vagyok
egyik vagyok
Udv egyik, en a masik vagyok
Szia masik
Szia masik
Amiközben egy további másik ablakból is, ugyancsak a telnet localhost 2006 parancs kiadásával csatlakozunk a
szerverhez:
C:\Documents and Settings\norbi> telnet localhost 2006
egyik vagyok
Udv egyik, en a masik vagyok
Udv egyik, en a masik vagyok
Szia masik
1.2.3.2. CORBA alapú csevegő
A CORBA alapú megoldásunkat a CORBA objektumok megtervezésével kezdjük. Itt természetesen az előző
pont tapasztalataiból indulunk ki: a szerverbe a kliensek be/ki tudjanak lépni és tudjanak üzenni. A szerver pedig
tudjon üzenni az összes bent lévő kliensnek. Egyszerű elképzelésünket az alábbi IDL interfészekkel írjuk le.
A Kliens interfésszel a kliens oldali, a Szerver interfésszel a szerver oldali CORBA objektumot írjuk le. A
Kliens objektumoknak csak üzenni lehet, a Szerver objektumba be és kilépni is.
1.2.3.2.1. A szerver és a kliens oldali IDL interfészek
module korba
{
interface Kliens
{
void uzenet(in string uzenet);
};
interface Szerver
{
void beszall(in Kliens kliens);
void kiszall(in Kliens kliens);
void uzenet(in string uzenet);
};
};
Az in szócska annyit jelent az IDL nyelven, hogy az utána szereplő paraméter a CORBA objektumba befelé
megy. A beszall(in Kliens kliens); metódus deklaráció például azt jelenti, hogy a Szerver CORBA
objektumnak átadjuk a kliens oldali Kliens CORBA objektum referenciáját.
A sikeres tervezőmunka után kiválaszthatjuk azt a programozási nyelvet, melyen az IDL interfészekben
megálmodott CORBA OO világunkat életre szeretnénk kelteni. A szóba jöhető nyelveket a [NYELVI
LEKÉPEZÉS] című lapon tanulmányozhatjuk. Esetünkben ez természetesen a Java. A JDK csomag részeként
kapott idlj fordító használatával tudjuk elvégezni a nyelvi leképezést, aminek során az idlj legenerálja a
Page 170
Saját világok teremtése és Java
alapok
142 Created by XMLmind XSL-FO Converter.
megfelelő CORBA specifikus Java forrásállományokat. Az IDL-Java leképezés például azt mondja, hogy az
IDL modul Java csomag lesz. Ennek megfelelően az idlj -fall csevego.idl parancs kiadása után korba
könyvtárban keletkeznek a POA objektumadaptereknek megfelelő SzerverPOA és KliensPOA osztályok. (A -
fall kapcsoló azt mondja a fordítónak, hogy a CORBA alapú kommunikációhoz szükséges, a kliens és a
szerveroldali Java forrásállományokat is generálja le.) Tipikus megoldás, hogy a CORBA objektumok
implementációit úgy készítjük el, hogy kiterjesztjük a megfelelő POA osztályokat. A következő két pontban is
ezt a módszert követjük.
1.2.3.2.2. A SzerverKiszolgáló osztály
A CORBA objektum elkészítésénél nincs más dolga a fejlesztőnek, mint a korba.SzerverPOA osztály
kiterjesztésében implementációt írni a Szerver interfészben deklarált három metódushoz:
void beszall(in Kliens kliens);
void kiszall(in Kliens kliens);
void uzenet(in string uzenet);
Vizsgáljuk meg például az elsőnek megfelelő Java metódus szignatúráját:
void beszall(korba.Kliens kliens);
A leképezést leginkább a szintén az idlj fordító generálta SzerverOperations Java interfészben érdemes
megfigyelni. Következzék most a CORBA kiszolgáló objektum teljes kódja:
/*
* SzerverKiszolgáló.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* A <code>csevego.idl</code> leírta Szerver CORBA
* kiszolgáló objektum megvalósítása.
*
* A <code>csevego.idl</code> interfész:
* <pre>
* module korba
* {
* interface Kliens
* {
* void uzenet(in string uzenet);
* };
*
* interface Szerver
* {
* void beszall(in Kliens kliens);
* void kiszall(in Kliens kliens);
* void uzenet(in string uzenet);
* };
* };
* </pre>
* A <code>korba.*</code> csomag legenerálása:
* <pre>
* idlj -fall csevego.idl
* </pre>
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
Page 171
Saját világok teremtése és Java
alapok
143 Created by XMLmind XSL-FO Converter.
* @see KorbaCsevegő
* @see korba.Szerver
* @see csevego.idl
*/
public class SzerverKiszolgáló
extends korba.SzerverPOA {
/** Itt tartjuk az éppen csevegő klienseket. */
private java.util.List<korba.Kliens> csevegők;
/** A <code>SzerverKiszolgáló</code> objektum elkászítése. */
public SzerverKiszolgáló() {
csevegők = new java.util.ArrayList<korba.Kliens>();
}
/**
* Egy CORBA kliens belép a csevegésbe.
*
* @param kliens a kliens CORBA objektum.
*/
public void beszall(korba.Kliens kliens) {
csevegők.add(kliens);
}
/**
* Egy CORBA kliens kilép a csevegésből.
*
* @param kliens a kliens CORBA objektum.
*/
public void kiszall(korba.Kliens kliens) {
csevegők.remove(kliens);
}
/**
* Egy CORBA kliens üzen a csevegőknek.
*
* @param üzenet az üzenet.
*/
public void uzenet(String üzenet) {
for(korba.Kliens csevegő: csevegők)
csevegő.uzenet(üzenet);
}
}
1.2.3.2.3. A KliensKiszolgáló osztály
Az osztály implementálásánál az előző ponthoz hasonlóan járunk el. Figyeljük meg: a királyi úton járást jelzi, ha
a kódban jóval több a megjegyzés, mint maga a tényleges kód!
/*
* KliensKiszolgáló.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* A <code>csevego.idl</code> leírta Kliens CORBA
* kiszolgáló objektum megvalósítása.
*
* A <code>csevego.idl</code> interfész:
* <pre>
* module korba
* {
* interface Kliens
* {
* void uzenet(in string uzenet);
Page 172
Saját világok teremtése és Java
alapok
144 Created by XMLmind XSL-FO Converter.
* };
*
* interface Szerver
* {
* void beszall(in Kliens kliens);
* void kiszall(in Kliens kliens);
* void uzenet(in string uzenet);
* };
* };
* </pre>
* A <code>korba.*</code> csomag legenerálása:
* <pre>
* idlj -fall csevego.idl
* </pre>
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see KorbaCsevegőKliens
* @see korba.Kliens
* @see csevego.idl
*/
public class KliensKiszolgáló
extends korba.KliensPOA {
/**
* Üzenetet kap a kliens, visszahívták.
*
* @param üzenet az üzenet.
*/
public void uzenet(String üzenet) {
System.out.println(üzenet);
}
}
1.2.3.2.4. A KorbaCsevegő osztály
A KorbaCsevegő a csevegőnk szerver oldala. Végrehajtja a szerver szokásos, a A KorbásLabirintus osztály
című pontban külön is részletezett teendőit: felveszi a kapcsolatot az ORB szoftverrel, elkészíti a kiszolgáló
CORBA objektumot, aminek tulajdonságait beállítja az objektumot éltető - POA - adapter segítségével, majd az
elkészített objektumot bejegyzi a CORBA objektumok telefonkönyvébe: a névszolgáltatóba.
/*
* KorbaCsevegő.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* Létrehozza és bejegyzi a névszolgáltatóba
* a CORBA Szervert kiszolgáló objektumot.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see KorbaCsevegőKliens
* @see korba.Szerver
* @see SzerverKiszolgáló
* @see csevego.idl
*/
public class KorbaCsevegő {
/** A <code>KorbaCsevegő</code> szerver indítása. */
public KorbaCsevegő() {
// A CORBA szerver indítása
try{
// Az ORB tulajdonságainak megadása
java.util.Properties orbTulajdonságok = new java.util.Properties();
orbTulajdonságok.put("org.omg.CORBA.ORBInitialHost", "localhost");
orbTulajdonságok.put("org.omg.CORBA.ORBInitialPort", "2006");
Page 173
Saját világok teremtése és Java
alapok
145 Created by XMLmind XSL-FO Converter.
// Az ORB (a metódushívások közvetítőjének) inicializálása
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init((String [])null,
orbTulajdonságok);
// A gyökér POA CORBA objektum referenciájának megszerzése
org.omg.CORBA.Object corbaObjektum =
orb.resolve_initial_references("RootPOA");
// CORBA-s típuskényszerítéssel a megfelelőre
org.omg.PortableServer.POA gyökérPoa =
org.omg.PortableServer.POAHelper.narrow(corbaObjektum);
// A POA kiszolgáló állapotba kapcsolása:
gyökérPoa.the_POAManager().activate();
// A kiszolgáló objektum létrehozása
SzerverKiszolgáló szerverKiszolgáló =
new SzerverKiszolgáló();
// CORBA objektum referencia elkészítése
corbaObjektum =
gyökérPoa.servant_to_reference(szerverKiszolgáló);
// CORBA-s típuskényszerítéssel a megfelelőre
korba.Szerver szerver =
korba.SzerverHelper.narrow(corbaObjektum);
// A névszolgáltató gyökerének, mint CORBA objektum
// referenciájának megszerzése
corbaObjektum =
orb.resolve_initial_references("NameService");
org.omg.CosNaming.NamingContextExt névszolgáltatóGyökere
= org.omg.CosNaming.NamingContextExtHelper
.narrow(corbaObjektum);
// A kiszolgáló objektum bejegyzése a névszolgáltatóba
org.omg.CosNaming.NameComponent név[] =
névszolgáltatóGyökere.to_name("Csevego");
névszolgáltatóGyökere.rebind(név, szerver);
// Várakozás a csevegők jelentkezésére
orb.run();
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
}
/** Elindítja a CORBA csevegő szervert. */
public static void main(String[] args) {
new KorbaCsevegő();
}
}
1.2.3.2.5. A KorbaCsevegőKliens osztály
A KorbaCsevegőKliens a csevegőnk kliens oldala. A kliensek szokásos: „a névszolgáltatáson keresztül
megszerzem a kívánt szolgáltatást nyújtó CORBA objektum referenciáját” tevékenységén túl a kód érdekessége,
hogy maga is elkészít és éltet egy CORBA objektumot, a visszahívható kliensoldali CORBA objektumot.
/*
* KorbaCsevegőKliens.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* Csatlakozika a CORBA szerverhez és elkészíti a klienst
* reprezentáló CORBA Kliens kiszolgáló objektumot.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see KorbaCsevegő
* @see korba.Kliens
Page 174
Saját világok teremtése és Java
alapok
146 Created by XMLmind XSL-FO Converter.
* @see KliensKiszolgáló
* @see csevego.idl
*/
public class KorbaCsevegőKliens {
/**
* Csatlakozik a névszolgáltatáson keresztül a
* szerverhez és elkészíti a visszahívható kliens objektumot.
*
* @param args[0] a csevegő beceneve.
*/
public static void main(String[] args) {
String becenév = null;
// ha nem adtuk meg a parancssor-argumentumot,
// akkor ez az alapértelmezés:
if(args.length != 1)
becenév = "Matyi";
else
becenév = args[0];
try {
// Az ORB tulajdonságainak megadása
java.util.Properties orbTulajdonságok = new java.util.Properties();
orbTulajdonságok.put("org.omg.CORBA.ORBInitialHost", "localhost");
orbTulajdonságok.put("org.omg.CORBA.ORBInitialPort", "2006");
// Az ORB (a metódushívások közvetítőjének) inicializálása
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init((String [])null,
orbTulajdonságok);
// A névszolgáltató gyökerének, mint CORBA objektum
// referenciájának megszerzése
org.omg.CORBA.Object corbaObjektum =
orb.resolve_initial_references("NameService");
org.omg.CosNaming.NamingContextExt névszolgáltatóGyökere
= org.omg.CosNaming.
NamingContextExtHelper.narrow(corbaObjektum);
// A Szerver szolgáltatást nyújtó CORBA objektum
// referenciájának megszerzése
korba.Szerver szerver =
korba.SzerverHelper.narrow(
névszolgáltatóGyökere.resolve_str("Csevego"));
// A kliens CORBA objektum létrehozása:
// A gyökér POA CORBA objektum referenciájának megszerzése
corbaObjektum =
orb.resolve_initial_references("RootPOA");
// CORBA-s típuskényszerítéssel a megfelelőre
org.omg.PortableServer.POA gyökérPoa =
org.omg.PortableServer.POAHelper.narrow(corbaObjektum);
// A POA kiszolgáló állapotba kapcsolása:
gyökérPoa.the_POAManager().activate();
// A kiszolgáló objektum létrehozása
KliensKiszolgáló kliensKiszolgáló =
new KliensKiszolgáló();
// CORBA objektum referencia elkészítése
corbaObjektum =
gyökérPoa.servant_to_reference(kliensKiszolgáló);
// CORBA-s típuskényszerítéssel a megfelelőre
korba.Kliens kliens =
korba.KliensHelper.narrow(corbaObjektum);
// A kliens átadja magát a szervernek
szerver.beszall(kliens);
// Olvasunk majd ezen a csatornán a csevegő billentyűzetéről
java.io.BufferedReader inputCsatorna =
new java.io.BufferedReader(
new java.io.InputStreamReader(System.in));
// amig a vege parancsot nem gépeli
String sor = inputCsatorna.readLine();
do {
if("vege".equals(sor))
break;
szerver.uzenet(becenév+"> "+sor);
Page 175
Saját világok teremtése és Java
alapok
147 Created by XMLmind XSL-FO Converter.
} while((sor = inputCsatorna.readLine()) != null );
// Kijelentkeztetjük magunkat
szerver.kiszall(kliens);
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.2.3.2.6. A csevegő kipróbálása
Elvégezzük az IDL-Java leképezést, majd lefordítjuk a forrásokat:
C:\... > idlj -fall csevego.idl
C:\... > javac KorbaCsevegő.java KorbaCsevegőKliens.java korba\*.java
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Indítjuk az ORB szoftvert:
C:\Documents and Settings\neuro>start orbd -ORBInitialPort 2006
Futtatjuk a csevegő szerverünket:
C:\... > java KorbaCsevegő
Egy másik ablakban egy klienst:
C:\... > java KorbaCsevegőKliens
Van bent valaki?
Matyi> Van bent valaki?
Erika> Igen, en itt vagyok!
miközben egy másik, további klienst is indítottunk:
C:\... > java KorbaCsevegőKliens Erika
Matyi> Van bent valaki?
Igen, en itt vagyok!
Erika> Igen, en itt vagyok!
Megjegyezhetjük, hogy a Java IDL használata mellett lehetőségünk van olyan kiszolgáló CORBA objektumok
létrehozására is, melyek túlélhetik az őket létrehozó szerverek élettartamát, ezek az úgynevezett perzisztens
objektumok. A majd ebben vagy általában a CORBA világa iránt mélyebben érdeklődő Olvasónak a Java
Page 176
Saját világok teremtése és Java
alapok
148 Created by XMLmind XSL-FO Converter.
dokumentáció docs\technotes\guides\idl\index.html, „Java IDL Technology” című lapját ajánljuk. (A
Java dokumentáció telepítéséről A Java dokumentáció, azaz az API doksi letöltése című pontban olvashatunk.)
Page 177
Created by XMLmind XSL-FO Converter.
II. rész - Programozás gépen
Ebben a részben nemcsak fejben és papíron, hanem főképp gépen dolgozunk! A következő labirintus példák
között találunk olyan, ami egy gépen, egy mobiltelefonon vagy akár több internetes gépen futtatható.
Page 178
150 Created by XMLmind XSL-FO Converter.
3. fejezet - Java esettanulmányok
„Tapasztalatunk szerint a program minőségének legjobb kritériuma az olvashatóság: ha egy program könnyen
olvasható, valószínűleg jó; ha nehezen olvasható, valószínűleg nem jó.” — Kernighan-Plauger A programozás magasiskolája
Az előző fejezetben kifejlesztett labirintus API számos használati esetét dolgozzuk fel ennek a résznek az
esettanulmányaiban. Kezdetben elkészítünk egy a „tiszta” teremtett OO világunkat bemutató karakteres példát,
majd egy böngészős, egy mobiltelefonos és számos más, különböző absztrakciós szinteken működő hálózati
példát.
Ebben a gyakorlati programozási részben a következő esettanulmányokat tárgyaljuk:
• tiszta OO labirintus
• böngészős és alkalmazásbeli labirintus
• játékszerű teljes képernyős labirintus
• mobiltelefonos labirintus
• szerveroldali
• TCP/IP
• Java Servlet
• Java RMI
• CORBA labirintus
• elosztott CORBA labirintus
1. Labirintus esettanulmányok Java nyelven
A fejezet esettanulmányaival való konkrét, gyakorlati tapasztalatszerzés első lépése a labirintusunk
mikrovilágának leírását tartalmazó javattanitok.labirintus csomagunk felélesztése, ennek menetét a
következő A labirintus API felélesztése című alpontban találjuk meg. Minden további esettanulmány ezt a
csomagot használja. A csomag kifejlesztését a következő pont részeként írjuk le.
1.1. A tiszta OO labirintus - Labirintus Világ
A példát az Előzetes a példaprogramokból című részben vezettük be. Majd a korábbi, a Java programozást
bevezető pontok példáiban már ennek a fejlesztési munkának a - sokszor leegyszerűsített - forráskód részleteit
mutattuk be. Itt elkészítjük labirintusunk végleges világát: magát a labirintust, kincsestől, szörnyestől, hősöstől,
a szokásos történettel: miszerint a hős bolyong, a szörnyek próbálják megenni, a kincs várja, hogy
rábukkanjanak. A megfelelő osztályokat a javattanitok.labirintus csomagba foglaljuk, azaz ebben a
csomagban fejlesztjük ki labirintusunk API interfészét.
1.1.1. A labirintus API felélesztése
Tetszőleges munkakönyvtárunkban hozzuk létre a javattanitok könyvtárat és azon belül a labirintus
könyvtárat!
C:\...\Munkakönyvtár> mkdir javattanitok
C:\...\Munkakönyvtár> mkdir javattanitok\labirintus
Page 179
Java esettanulmányok
151 Created by XMLmind XSL-FO Converter.
Ezzel a javattanitok.labirintus Java csomagot leképeztük a munkakönyvtárunkra, a kifejlesztendő
osztályokat ide, a javattanitok/labirintus könyvtárban írjuk majd meg, azaz gyűjtük most össze. Másoljuk
ide a következő Szereplő.java, Hős.java, Kincs.java, Szörny.java, Labirintus.java,
RosszLabirintusKivétel.java állományokat!
Az alábbi állományok kimásolása után a A labirintus API fordítása című pontban folytatjuk a labirintus API
lefordításával. (Az állományok A példaprogramok forrásainak letöltése című pontban ismertetett archívumban
is megtalálhatóak.)
1.1.1.1. A Szereplő osztály
A Szereplő osztály írását a Saját szereplők feladat című pontban kezdtük el fejleszteni. Íme itt a teljes kód:
/*
* Szereplő.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok.labirintus;
/**
* A labirintus szereplőit (kincsek, szörnyek, hős) absztraháló osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.labirintus.Labirintus
*/
public class Szereplő {
/** A szereplő oszlop pozíciója. */
protected int oszlop;
/** A szereplő sor pozíciója. */
protected int sor;
/** A labirintus, melyben a szereplő van. */
protected Labirintus labirintus;
/** A labirintus szélessége. */
protected int maxSzélesség;
/** A labirintus magassága. */
protected int maxMagasság;
/** Véletlenszám generátor a szereplők mozgatásához. */
protected static java.util.Random véletlenGenerátor
= new java.util.Random();
/**
* Létrehoz egy <code>Szereplő</code> objektumot.
*
* @param labirintus amibe a szereplőt helyezzük.
*/
public Szereplő(Labirintus labirintus) {
this.labirintus = labirintus;
maxSzélesség = labirintus.szélesség();
maxMagasság = labirintus.magasság();
// A szereplő kezdő pozíciója a labirintusban
szereplőHelyeKezdetben();
}
/**
* A szereplő labirintusbeli kezdő pozíciójának meghatározása.
*/
void szereplőHelyeKezdetben() {
// Többször próbálkozunk elhelyezni a szereplőt a labirintusban,
// számolja, hol tartunk ezekkel a próbálkozásokkal:
int számláló = 0;
do {
// itt +2,-2-k, hogy a bal alsó saroktól távol tartsuk
// a szereplőket, mert majd ezt akarjuk a hős kezdő pozíciójának
oszlop = 2+véletlenGenerátor.nextInt(maxSzélesség-2);
sor = véletlenGenerátor.nextInt(maxMagasság-2);
// max. 10-szer próbálkozunk, de ha sikerül nem "falba tenni" a
Page 180
Java esettanulmányok
152 Created by XMLmind XSL-FO Converter.
// szereplőt, akkor máris kilépünk:
} while(++számláló<10 && labirintus.fal(oszlop, sor));
}
/**
* A szereplő felfelé lép. Ha nem tud, helyben marad.
*/
public void lépFöl() {
if(sor > 0)
if(!labirintus.fal(oszlop, sor-1))
--sor;
}
/**
* A szereplő lefelé lép. Ha nem tud, helyben marad.
*/
public void lépLe() {
if(sor < maxMagasság-1)
if(!labirintus.fal(oszlop, sor+1))
++sor;
}
/**
* A szereplő balra lép. Ha nem tud, helyben marad.
*/
public void lépBalra() {
if(oszlop > 0)
if(!labirintus.fal(oszlop-1, sor))
--oszlop;
}
/**
* A szereplő jobbra lép. Ha nem tud, helyben marad.
*/
public void lépJobbra() {
if(oszlop < maxSzélesség-1)
if(!labirintus.fal(oszlop+1, sor))
++oszlop;
}
/**
* A szereplő (Euklideszi) távolsága egy másik szereplőtől a labirintusban.
*
* @param szereplő a másik szereplő
* @return int távolság a másik szereplőtől.
*/
public int távolság(Szereplő szereplő) {
return (int)Math.sqrt((double)
(oszlop - szereplő.oszlop)*(oszlop - szereplő.oszlop)
+ (sor - szereplő.sor)*(sor - szereplő.sor)
);
}
/**
* Egy pozíció (Euklideszi) távolsága egy másik szereplőtől a
* labirintusban.
*
* @param oszlop a pozíció oszlop koordinátája
* @param sor a pozíció sor koordinátája
* @param szereplő a másik szereplő
* @return int távolság a másik szereplőtől.
*/
public int távolság(int oszlop, int sor, Szereplő szereplő) {
if(!(oszlop >= 0 && oszlop <= maxSzélesség-1
&& sor >= 0 && sor <= maxMagasság-1))
return Integer.MAX_VALUE;
else
Page 181
Java esettanulmányok
153 Created by XMLmind XSL-FO Converter.
return (int)Math.sqrt((double)
(oszlop - szereplő.oszlop)*(oszlop - szereplő.oszlop)
+ (sor - szereplő.sor)*(sor - szereplő.sor)
);
}
/**
* Beállítja a szereplő labirintusbeli pozíciójának oszlop
* koordinátáját.
*
* @param oszlop a szereplő labirintusbeli pozíciójának oszlop
* koordinátája.
*/
public void oszlop(int oszlop) {
this.oszlop = oszlop;
}
/**
* Beállítja a szereplő labirintusbeli pozíciójának sor koordinátáját.
*
* @param sor a szereplő labirintusbeli pozíciójának sor koordinátája.
*/
public void sor(int sor) {
this.sor = sor;
}
/**
* Megadja a szereplő labirintusbeli pozíciójának oszlop koordinátáját.
*
* @return int a szereplő labirintusbeli pozíciójának oszlop koordinátája.
*/
public int oszlop() {
return oszlop;
}
/**
* Megadja a szereplő labirintusbeli pozíciójának sor koordinátáját.
*
* @return int a szereplő labirintusbeli pozíciójának sor koordinátája.
*/
public int sor() {
return sor;
}
public String toString() {
return "SZEREPLŐ oszlop = "
+ oszlop
+ " sor = "
+ sor;
}
}
A Szereplő osztály tagként tartalmazza annak a labirintusnak a referenciáját, amelybe belehelyezzük. Ez furcsa
lehet annak az Olvasónak, aki gondolkodása szerint a labirintusnak van szereplője és nem a szereplőnek
labirintusa. Viszont az első esetben, tehát ha a Szereplő osztályunk ezt a tagot nem tartalmazná, akkor a
szereplő mozgását adó metódusoknak mindig át kellene adni a labirintus referenciáját is, hogy ezek a mozgató
függvények tudják, le tudják kérdezni, hogy hová léptethetik a szereplőt.
1.1.1.2. A Hős osztály
Ez a pont folytatása Az osztályok fejlődése: az öröklődés című pontban megkezdett, a labirintus hősét
boncolgató gondolatmenetnek.
Page 182
Java esettanulmányok
154 Created by XMLmind XSL-FO Converter.
/*
* Hős.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok.labirintus;
/**
* A labirintus hősét leíró osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.labirintus.Labirintus
*/
public class Hős extends Szereplő {
/** A labirintusban megtalált kincsek értékei. */
protected int megtaláltÉrtékek;
/** A hős életeinek maximális száma. */
public static final int ÉLETEK_SZÁMA = 5;
/** A hős életeinek száma. */
protected int életekSzáma = ÉLETEK_SZÁMA;
/**
* Létrehoz egy <code>Hős</code> objektumot.
*
* @param labirintus amelyben a hős bolyongani fog.
*/
public Hős(Labirintus labirintus) {
super(labirintus);
megtaláltÉrtékek = 0;
}
/**
* Gyüjtögeti a megtalált kincseket.
*
* @param kincs amit éppen magtalált a hős.
*/
public void megtaláltam(Kincs kincs) {
megtaláltÉrtékek += kincs.érték();
}
/**
* Jelzi, hogy éppen megettek.
*
* @return true ha a hősnek még van élete, ellenkező esetben,
* azaz ha az összes élete elfogyott már, akkor false.
*/
public boolean megettek() {
if(életekSzáma > 0) {
--életekSzáma;
return false;
} else
return true;
}
/**
* megmondja, hogy élek-e még?
*
* @return true ha a hősnek még van élete, ellenkező esetben, azaz
* ha az összes élete elfogyott már, akkor false.
*/
public boolean él() {
return életekSzáma > 0;
}
/**
* Megadja az életek számát.
*
Page 183
Java esettanulmányok
155 Created by XMLmind XSL-FO Converter.
* @return int az életek száma.
*/
public int életek() {
return életekSzáma;
}
/**
* Megadja a megtalált kincsek összegyüjtögetett értékeit.
*
* @return int a megtalált kincsek összegyüjtögetett értékei.
*/
public int pontszám() {
return megtaláltÉrtékek;
}
/**
* A labirintus, amiben a hős mozog.
*
* @return Labirintus a labirintus.
*/
public Labirintus labirintus() {
return labirintus;
}
}
1.1.1.3. A Kincs osztály
A Kincs osztály írását a Saját szereplők feladat című pontban kezdtük el fejleszteni. Íme itt a teljes kód:
/*
* Kincs.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok.labirintus;
/**
* A labirintus kincseit jellemző osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.labirintus.Labirintus
*/
public class Kincs extends Szereplő {
/** A kincs értéke. */
protected int érték;
/** Megtaláltak már? */
protected boolean megtalálva;
/**
* Létrehoz egy {@code Kincs} objektumot.
*
* @param labirintus amibe a kincset helyezzük.
* @param érték a kincs értéke.
*/
public Kincs(Labirintus labirintus, int érték) {
super(labirintus);
this.érték = érték;
}
/**
* A szereplő (pl. hős, szörnyek) megtalálta a kincset?
*
* @param hős aki keresi a kincset.
Page 184
Java esettanulmányok
156 Created by XMLmind XSL-FO Converter.
* @return true ha a kincset éppen most megtalálta a szereplő,
* ha éppen nem, vagy már eleve megvan találva a kincs, akkor false.
*/
public boolean megtalált(Szereplő szereplő) {
if(megtalálva)
// mert egy kicset csak egyszer lehet megtalálni
return false;
if(oszlop == szereplő.oszlop()
&& sor == szereplő.sor())
megtalálva = true;
return megtalálva;
}
/**
* Megadja a kincs értékét.
*
* @return int a kincs értéke.
*/
public int érték() {
return érték;
}
/**
* Megmondja, hogy megtalálták-e már a kincset?
*
* @return true ha a kincset már megtalálták,
* ha még nem akkor false.
*/
public boolean megtalálva() {
return megtalálva;
}
/**
* A {@code Kincs} objektum sztring reprezentációját adja
* meg.
*
* @return String az objektum sztring reprezentációja.
*/
public String toString() {
return "KINCS érték = "
+ érték
+ " megtalálva = "
+ megtalálva;
}
}
1.1.1.4. A Szörny osztály
A Szörny osztály írását a Saját szereplők feladat című pontban kezdtük el fejleszteni. Íme itt a teljes kód:
/*
* Szörny.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok.labirintus;
/**
* A labirintus szörnyeit megadó osztály.
*
* @author Bátfai Norbert, [email protected]
Page 185
Java esettanulmányok
157 Created by XMLmind XSL-FO Converter.
* @version 0.0.1
* @see javattanitok.labirintus.Labirintus
*/
public class Szörny extends Szereplő {
/** A megevett hősök száma. */
int megevettHősökSzáma;
/** A megevett kincsek száma. */
int megevettKincsekSzáma;
/**
* Létrehoz egy <code>Szörny</code> objektumot.
*
* @param labirintus amibe a szörnyet helyezzük.
*/
public Szörny(Labirintus labirintus) {
super(labirintus);
}
/**
* A szörnyek mozgásának vezérlése, ami szerint szörnyek
* a hős felés igyekeznek.
*
* @param hős aki felé a szörny igyekszik
*/
public void lép(Hős hős) {
int távolság = távolság(hős);
// Abba az irányba lévő pozícióra lép, amelyben közelebb kerül a hős.
if(!labirintus.fal(oszlop, sor-1))
if(távolság(oszlop, sor-1, hős) < távolság) {
lépFöl();
return;
}
if(!labirintus.fal(oszlop, sor+1))
if(távolság(oszlop, sor+1, hős) < távolság) {
lépLe();
return;
}
if(!labirintus.fal(oszlop-1, sor))
if(távolság(oszlop-1, sor, hős) < távolság) {
lépBalra();
return;
}
if(!labirintus.fal(oszlop+1, sor))
if(távolság(oszlop+1, sor, hős) < távolság) {
lépJobbra();
return;
}
}
/**
* A szörny megette a hőst?
*
* @param hős aki bolyong a labirintusban.
*/
public boolean megesz(Hős hős) {
if(oszlop == hős.oszlop()
&& sor == hős.sor()) {
++megevettHősökSzáma;
return true;
} else
return false;
}
/**
Page 186
Java esettanulmányok
158 Created by XMLmind XSL-FO Converter.
* Számolgatja a megevett kincseket.
*
* @param kincs amit éppen megettem.
*/
public void megtaláltam(Kincs kincs) {
++megevettKincsekSzáma;
}
/**
* A {@code Szörny} objektum sztring reprezentációját adja
* meg.
*
* @return String az objektum sztring reprezentációja.
*/
public String toString() {
return "SZÖRNY megevett hősök = "
+ megevettHősökSzáma
+ "megevett kincsek = "
+ megevettKincsekSzáma;
}
}
1.1.1.5. A Labirintus osztály
A Labirintus osztály írását a Saját labirintus feladat című pontban kezdtük el fejleszteni. Íme itt a teljes kód:
/*
* Labirintus.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok.labirintus;
/**
* A labirintust leíró osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
*/
public class Labirintus {
/** A labirintus szélessége. */
protected int szélesség;
/** A labirintus magassága. */
protected int magasság;
/** A labirintus szerkezete: hol van fal, hol járat. */
protected boolean[][] szerkezet;
/** A falat a true érték jelenti. */
final static boolean FAL = true;
/** Milyen állapotban van éppen a játék. */
protected int játékÁllapot = 0;
/** Normál működés, a hőssel időközben semmi nem történt. */
public static final int JÁTÉK_MEGY_HŐS_RENDBEN = 0;
/** A hőst éppen megették, de még van élete. */
public final static int JÁTÉK_MEGY_MEGHALT_HŐS = 1;
/** Vége a játéknak, a játékos győzött. */
public final static int JÁTÉK_VÉGE_MINDEN_KINCS_MEGVAN = 2;
/** Vége a játéknak, a játékos vesztett. */
public final static int JÁTÉK_VÉGE_MEGHALT_HŐS = 3;
/** A labirintus kincsei. */
protected Kincs [] kincsek;
/** A labirintus szörnyei. */
protected Szörny [] szörnyek;
/**
* Létrehoz egy megadott szerkezetű
Page 187
Java esettanulmányok
159 Created by XMLmind XSL-FO Converter.
* {@code Labirintus} objektumot.
*/
public Labirintus() {
szerkezet = new boolean[][]{
{false, false, false, true, false, true, false, true, true, true },
{false, false, false, false, false, false, false, false, false, false},
{true, false, true, false, true, false, true, false, true, false},
{false, false, false, false, true, false, true, false, false, false},
{false, true, true, false, false, false, true, true, false, true },
{false, false, false, false, true, false, false, false, false, false},
{false, true, false, false, false, true, false, true, true, false},
{false, false, false, true, false, true, false, true, false, false},
{false, true, false, false, false, false, false, false, false, true },
{false, false, false, false, true, false, false, false, true, true }
};
magasság = szerkezet.length;
szélesség = szerkezet[0].length;
}
/**
* Létrehoz egy paraméterben kapott szerkezetű <code>Labirintus</code>
* objektumot.
*
* @param kincsekSzáma a kincsek száma a labirintusban.
* @param szörnyekSzáma a szörnyek száma a labirintusban.
* @exception RosszLabirintusKivétel ha a labirintust definiáló tömb
* nincs elkészítve.
*/
public Labirintus(boolean[][] szerkezet, int kincsekSzáma, int szörnyekSzáma)
throws RosszLabirintusKivétel {
if(szerkezet == null)
throw new RosszLabirintusKivétel("A labirintust definiáló tömb nincs
elkészítve.");
this.szerkezet = szerkezet;
magasság = szerkezet.length;
szélesség = szerkezet[0].length;
kincsekSzörnyek(kincsekSzáma, szörnyekSzáma);
}
/**
* Létrehoz egy megadott méretű, véletlen szerkezetű
* <code>Labirintus</code> objektumot.
*
* @param szélesség a labirintus szélessége.
* @param magasság a labirintus magassága.
* @param kincsekSzáma a kincsek száma a labirintusban.
* @param szörnyekSzáma a szörnyek száma a labirintusban.
*/
public Labirintus(int szélesség, int magasság,
int kincsekSzáma, int szörnyekSzáma) {
this.magasság = magasság;
this.szélesség = szélesség;
szerkezet = new boolean[magasság][szélesség];
java.util.Random véletlenGenerátor = new java.util.Random();
for(int i=0; i<magasság; ++i)
for(int j=0; j<szélesség; ++j)
if(véletlenGenerátor.nextInt()%3 == 0)
// a labirintus egy harmada lesz fal
szerkezet[magasság][szélesség] = false;
else
// két harmada pedig járat
szerkezet[magasság][szélesség] = true;
Page 188
Java esettanulmányok
160 Created by XMLmind XSL-FO Converter.
kincsekSzörnyek(kincsekSzáma, szörnyekSzáma);
}
/**
* Létrehoz egy 10x10-es, beépített szerkezetű <code>Labirintus</code>
* objektumot.
*
* @param kincsekSzáma a kincsek száma a labirintusban.
* @param szörnyekSzáma a szörnyek száma a labirintusban.
*/
public Labirintus(int kincsekSzáma, int szörnyekSzáma) {
this();
magasság = szerkezet.length;
szélesség = szerkezet[0].length;
kincsekSzörnyek(kincsekSzáma, szörnyekSzáma);
}
/**
* Egy megfelelő szerkezetű szöveges állományból elkészít egy új a
* <code>Labirintus</code> objektumot.
* A szöveges állomány szerkezete a következő:
* <pre>
* // A labirintus szerkezetét megadó állomány, szerkezete a következő:
* // a kincsek száma
* // a szörnyek száma
* // a labirintus szélessége
* // magassága
* // fal=1 járat=0 ...
* // .
* // .
* // .
* 6
* 3
* 10
* 10
* 0 0 0 1 0 1 0 1 1 1
* 0 0 0 0 0 0 0 0 0 0
* 1 0 1 0 1 0 1 0 1 0
* 0 0 0 0 1 0 1 0 0 0
* 0 1 1 0 0 0 1 1 0 1
* 0 0 0 0 1 0 0 0 0 0
* 0 1 0 0 0 1 0 1 1 0
* 0 0 0 1 0 1 0 1 0 0
* 0 1 0 0 0 0 0 0 0 1
* 0 0 0 0 1 0 0 0 1 1
* </pre>
*
* @param labirintusFájlNév a labirintust definiáló, megfelelő
* szerkezetű szöveges állomány neve.
* @exception RosszLabirintusKivétel ha a labirintust definiáló állomány
* nincs meg, nem a megfelelő szerkezetű, vagy gond van az olvasásával.
*/
public Labirintus(String labirintusFájlNév) throws RosszLabirintusKivétel {
int kincsekSzáma = 6; // ezeknek a kezdőértékeknek nincs jelentősége,
int szörnyekSzáma = 3; // mert majd a fájlból olvassuk be, amiben ha a
// négy fő adat hibás, akkor nem is építjük fel a labirintust.
// Csatorna a szöveges állomány olvasásához
java.io.BufferedReader szövegesCsatorna = null;
try {
szövegesCsatorna = new java.io.BufferedReader(
new java.io.FileReader(labirintusFájlNév));
String sor = szövegesCsatorna.readLine();
while(sor.startsWith("//"))
Page 189
Java esettanulmányok
161 Created by XMLmind XSL-FO Converter.
sor = szövegesCsatorna.readLine();
try {
kincsekSzáma = Integer.parseInt(sor);
sor = szövegesCsatorna.readLine();
szörnyekSzáma = Integer.parseInt(sor);
sor = szövegesCsatorna.readLine();
szélesség = Integer.parseInt(sor);
sor = szövegesCsatorna.readLine();
magasság = Integer.parseInt(sor);
szerkezet = new boolean[magasság][szélesség];
} catch(java.lang.NumberFormatException e) {
throw new RosszLabirintusKivétel("Hibás a kincsek, szörnyek száma,
szélesség, magasság megadási rész.");
}
for(int i=0; i<magasság; ++i) {
sor = szövegesCsatorna.readLine();
java.util.StringTokenizer st =
new java.util.StringTokenizer(sor);
for(int j=0; j<szélesség; ++j) {
String tegla = st.nextToken();
try {
if(Integer.parseInt(tegla) == 0)
szerkezet[i][j] = false;
else
szerkezet[i][j] = true;
} catch(java.lang.NumberFormatException e) {
System.out.println(i+". sor "+j+". oszlop "+e);
szerkezet[i][j] = false;
}
}
}
} catch(java.io.FileNotFoundException e1) {
throw new RosszLabirintusKivétel("Nincs meg a fájl: " + e1);
} catch(java.io.IOException e2) {
throw new RosszLabirintusKivétel("IO kivétel történt: "+e2);
} catch(java.util.NoSuchElementException e3) {
throw new RosszLabirintusKivétel("Nem jó a labirintus szerkezete: "
+e3);
} finally {
if(szövegesCsatorna != null) {
try{
szövegesCsatorna.close();
} catch(Exception e) {}
}
Page 190
Java esettanulmányok
162 Created by XMLmind XSL-FO Converter.
}
// Ha ide eljutottunk, akkor felépült a labirintus,
// lehet benépesíteni:
kincsekSzörnyek(kincsekSzáma, szörnyekSzáma);
}
/**
* Létrehozza a kincseket és a szörnyeket.
*
* @param kincsekSzáma a kincsek száma a labirintusban.
* @param szörnyekSzáma a szörnyek száma a labirintusban.
*/
private void kincsekSzörnyek(int kincsekSzáma, int szörnyekSzáma) {
// Kincsek létrehozása
kincsek = new Kincs[kincsekSzáma];
for(int i=0; i<kincsek.length; ++i)
kincsek[i] = new Kincs(this, (i+1)*100);
// Szörnyek létrehozása
szörnyek = new Szörny[szörnyekSzáma];
for(int i=0; i<szörnyek.length; ++i)
szörnyek[i] = new Szörny(this);
}
/**
* Megadja a játék aktuális állapotát.
*
* @return int a játék aktuális állapota.
*/
public int állapot() {
return játékÁllapot;
}
/**
* A labirintus mikrovilág életének egy pillanata: megnézi, hogy a bolyongó
* hős rátalált-e a kincsekre, vagy a szörnyek a hősre. Ennek megfelelően
* megváltozik a játék állapota.
*
* @param hős aki a labirintusban bolyong.
* @return int a játék állapotát leíró kód.
*/
public int bolyong(Hős hős) {
boolean mindMegvan = true;
for(int i=0; i < kincsek.length; ++i) {
// A hős rátalált valamelyik kincsre?
if(kincsek[i].megtalált(hős))
hős.megtaláltam(kincsek[i]);
// ha ez egyszer is teljesül, akkor nincs minden kincs megtalálva
if(!kincsek[i].megtalálva())
mindMegvan = false;
}
if(mindMegvan) {
játékÁllapot = JÁTÉK_VÉGE_MINDEN_KINCS_MEGVAN;
return játékÁllapot;
}
for(int i=0; i < szörnyek.length; ++i) {
szörnyek[i].lép(hős);
if(szörnyek[i].megesz(hős)) {
játékÁllapot = JÁTÉK_MEGY_MEGHALT_HŐS;
Page 191
Java esettanulmányok
163 Created by XMLmind XSL-FO Converter.
if(hős.megettek())
játékÁllapot = JÁTÉK_VÉGE_MEGHALT_HŐS;
return játékÁllapot;
}
}
return JÁTÉK_MEGY_HŐS_RENDBEN;
}
/**
* Madadja, hogy fal-e a labirintus adott oszlop, sor pozíciója.
*
* @param oszlop a labirintus adott oszlopa
* @param sor a labirintus adott sora
* @return true ha a pozíció fal vagy nincs a labirintusban.
*/
public boolean fal(int oszlop, int sor) {
if(!(oszlop >= 0 && oszlop <= szélesség-1
&& sor >= 0 && sor <= magasság-1))
return FAL;
else
return szerkezet[sor][oszlop] == FAL;
}
/**
* Madadja a labirintus szélességét.
*
* @return int a labirintus szélessége.
*/
public int szélesség() {
return szélesség;
}
/**
* Madadja a labirintus magasságát.
*
* @return int a labirintus magassága.
*/
public int magasság() {
return magasság;
}
/**
* Megadja a labirintus szerkezetét.
*
* @return boolean[][] a labirintus szerkezete.
*/
public boolean[][] szerkezet() {
return szerkezet;
}
/**
* Megadja a labirintus kincseit.
*
* @return Kincs[] a labirintus kincsei.
*/
public Kincs[] kincsek() {
return kincsek;
}
/**
* Megadja a labirintus szörnyeit.
*
* @return Szörny[] a labirintus szörnyei.
*/
Page 192
Java esettanulmányok
164 Created by XMLmind XSL-FO Converter.
public Szörny[] szörnyek() {
return szörnyek;
}
/**
* Kinyomtatja a labirintus szerkezetét a System.out-ra.
*/
public void nyomtat() {
for(int i=0; i<magasság; ++i) {
for(int j=0; j<szélesség; ++j) {
if(szerkezet[i][j])
System.out.print("|FAL");
else
System.out.print("| ");
}
System.out.println();
}
}
/**
* Kinyomtatja a labirintus szerkezetét és szereplőit a System.out-ra.
*
* @param hős akit szintén belenyomtat a labirintusba.
*/
public void nyomtat(Hős hős) {
for(int i=0; i<magasság; ++i) {
for(int j=0; j<szélesség; ++j) {
boolean vanSzörny = vanSzörny(i, j);
boolean vanKincs = vanKincs(i, j);
boolean vanHős = (i == hős.sor() && j == hős.oszlop());
if(szerkezet[i][j])
System.out.print("|FAL");
else if(vanSzörny && vanKincs && vanHős)
System.out.print("|SKH");
else if(vanSzörny && vanKincs)
System.out.print("|SK ");
else if(vanKincs && vanHős)
System.out.print("|KH ");
else if(vanSzörny && vanHős)
System.out.print("|SH ");
else if(vanKincs)
System.out.print("|K ");
else if(vanHős)
System.out.print("|H ");
else if(vanSzörny)
System.out.print("|S ");
else
System.out.print("| ");
}
System.out.println();
}
}
/**
* Kinyomtatja a labirintus szerkezetét és szereplőit egy
* karakteres csatornába.
*
* @param hős akit szintén belenyomtat a labirintusba.
* @param csatorna ahova nyomtatunk.
*/
Page 193
Java esettanulmányok
165 Created by XMLmind XSL-FO Converter.
public void nyomtat(Hős hős, java.io.PrintWriter csatorna) {
for(int i=0; i<magasság; ++i) {
for(int j=0; j<szélesség; ++j) {
boolean vanSzörny = vanSzörny(i, j);
boolean vanKincs = vanKincs(i, j);
boolean vanHős = (i == hős.sor() && j == hős.oszlop());
if(szerkezet[i][j])
csatorna.print("|FAL");
else if(vanSzörny && vanKincs && vanHős)
csatorna.print("|SKH");
else if(vanSzörny && vanKincs)
csatorna.print("|SK ");
else if(vanKincs && vanHős)
csatorna.print("|KH ");
else if(vanSzörny && vanHős)
csatorna.print("|SH ");
else if(vanKincs)
csatorna.print("|K ");
else if(vanHős)
csatorna.print("|H ");
else if(vanSzörny)
csatorna.print("|S ");
else
csatorna.print("| ");
}
csatorna.println();
}
}
/**
* Kinyomtatja a labirintus szerkezetét és szereplőit egy sztringbe.
*
* @param hős akit szintén belenyomtat a labirintusba.
* @return String a kinyomtatott labirintus
*/
public String kinyomtat(Hős hős) {
StringBuffer stringBuffer = new StringBuffer();
for(int i=0; i<magasság; ++i) {
for(int j=0; j<szélesség; ++j) {
boolean vanSzörny = vanSzörny(i, j);
boolean vanKincs = vanKincs(i, j);
boolean vanHős = (i == hős.sor() && j == hős.oszlop());
if(szerkezet[i][j])
stringBuffer.append("|FAL");
else if(vanSzörny && vanKincs && vanHős)
stringBuffer.append("|SKH");
else if(vanSzörny && vanKincs)
stringBuffer.append("|SK ");
else if(vanKincs && vanHős)
stringBuffer.append("|KH ");
else if(vanSzörny && vanHős)
stringBuffer.append("|SH ");
else if(vanKincs)
stringBuffer.append("|K ");
else if(vanHős)
stringBuffer.append("|H ");
else if(vanSzörny)
stringBuffer.append("|S ");
else
stringBuffer.append("| ");
}
Page 194
Java esettanulmányok
166 Created by XMLmind XSL-FO Converter.
stringBuffer.append("\n");
}
return stringBuffer.toString();
}
/**
* Madadja, hogy van-e megtalálható kincs a labirintus
* adott oszlop, sor pozíciója.
*
* @param oszlop a labirintus adott oszlopa
* @param sor a labirintus adott sora
* @return true ha van.
*/
boolean vanKincs(int sor, int oszlop) {
boolean van = false;
for(int i=0; i<kincsek.length; ++i)
if(sor == kincsek[i].sor()
&& oszlop == kincsek[i].oszlop()
&& !kincsek[i].megtalálva()) {
van = true;
break;
}
return van;
}
/**
* Madadja, hogy van-e szörny a labirintus adott oszlop,
* sor pozíciója.
*
* @param oszlop a labirintus adott oszlopa
* @param sor a labirintus adott sora
* @return true ha van.
*/
boolean vanSzörny(int sor, int oszlop) {
boolean van = false;
for(int i=0; i<szörnyek.length; ++i)
if(sor == szörnyek[i].sor()
&& oszlop == szörnyek[i].oszlop()) {
van = true;
break;
}
return van;
}
/**
* A labirintussal kapcsolatos apróságok önálló kipróbálására
* szolgál ez az indító metódus.
*
* @param args parancssor-argumentumok nincsenek.
*/
public static void main(String[] args) {
Labirintus labirintus = new Labirintus(6, 3);
Hős hős = new Hős(labirintus);
System.out.println(labirintus.getClass());
System.out.println(hős.getClass());
}
}
1.1.1.6. A RosszLabirintusKivétel osztály
Page 195
Java esettanulmányok
167 Created by XMLmind XSL-FO Converter.
A RosszLabirintusKivétel osztály írását a Kivételkezelés című pontban kezdtük el fejleszteni. Íme itt a teljes
kód:
/*
* RosszLabirintusKivétel.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok.labirintus;
/**
* Ha állomány alapján készítjük a labirintust, akkor az állomány szerkezetének
* hibáit jelzi ez az osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.labirintus.Labirintus
*/
public class RosszLabirintusKivétel extends java.lang.Exception {
/**
* Elkészít egy <code>RosszLabirintusKivétel</code> kivétel objektumot.
*
* @param hiba a hiba leírása
*/
public RosszLabirintusKivétel(String hiba) {
super(hiba);
}
}
1.1.1.7. A labirintus API fordítása
C:\...\Munkakönyvtár> javac javattanitok\labirintus\*.java
Ha a korábbi Java osztályok kódját hiba nélkül jelöltük ki és illesztettük be a megfelelő állományokba, akkor ez
a fordítási parancs gond nélkül lefutott és a javattanitok/labirintus könyvtárban létrejöttek az
osztályoknak megfelelő .class állományok.
1.1.2. A LabirintusVilág osztály
Ebben a LabirintusVilág nevű osztályban keltjük életre az előző pontban kifejlesztett
javattanitok.labirintus API csomagot. Csupán egy karakteres megjelenést készítünk, ami jól mutatja,
hogyan kel életre a teremtett tiszta OO mikrovilágunk: a labirintus.
Nincs más dolgunk, mint a következő LabirintusVilág osztályt a javattanitok könyvtárba bemásolni.
(Általában is úgy szervezzük a kódokat, hogy a javattanitok.labirintus API csomagot használó
esettanulmányok osztályait a javattanitok csomagba helyezzük el.)
1.1.2.1. A LabirintusVilág osztály
/*
* LabirintusVilág.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
Page 196
Java esettanulmányok
168 Created by XMLmind XSL-FO Converter.
import javattanitok.labirintus.*;
/**
* A labirintus csomag absztrahálta labirintus mikrovilágának egy
* "tiszta" életre keltésére ad példát ez az osztály. Ennek megfelelően
* csak egyszerű karakteres megjelenítést biztosít. Fő feladata a
* kialakított labirintus OO mikrovilágunk API interfésze használatának
* bemutatása. Továbbá az egyszerűség megtartása miatt ebben a példában
* még nem vesz át adatokat a játékostól a program.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.LabirintusApplet
* @see javattanitok.LabirintusJáték
* @see javattanitok.LabirintusMIDlet
* @see javattanitok.LabirintusServlet
* @see javattanitok.HálózatiLabirintus
* @see javattanitok.TávoliLabirintus
* @see javattanitok.KorbásLabirintus
* @see javattanitok.ElosztottLabirintus
*/
public class LabirintusVilág implements Runnable {
/** A labirintus. */
protected Labirintus labirintus;
/** A hős. */
protected Hős hős;
/** A játékbeli idő mérésére.*/
private long idő = 0;
public LabirintusVilág() {
}
/**
* A <code>LabirintusVilág</code> objektum elkészítése.
*
* @param labirintusFájlNév a labirintust definiáló, megfelelő
* szerkezetű szöveges állomány neve.
* @exception RosszLabirintusKivétel ha a labirintust definiáló állomány nem
* a megfelelő szerkezetű
*/
public LabirintusVilág(String labirintusFájlNév)
throws RosszLabirintusKivétel {
// A labirintus elkészítése állományból
labirintus = new Labirintus(labirintusFájlNév);
// A hős elkészítése és a kezdő pozíciójának beállítása
hős = new Hős(labirintus);
hős.sor(9);
hős.oszlop(0);
// A játékbeli idő folyását biztosító szál elkészítése és indítása
new Thread(this).start();
}
/**
* A játék időbeli fejlődésének vezérlése. A labirintus mikrovilágának
* jelen osztálybeli életre keltésében max. 10 időpillanatig játszunk,
* mialatt a hős igyekszik mindig jobbra lépni.
*/
public void run() {
labirintus.nyomtat();
boolean játékVége = false;
while(!játékVége) {
idoegyseg();
if(idő<10)
hős.lépJobbra();
else
break;
Page 197
Java esettanulmányok
169 Created by XMLmind XSL-FO Converter.
switch(labirintus.bolyong(hős)) {
case Labirintus.JÁTÉK_MEGY_HŐS_RENDBEN:
break;
case Labirintus.JÁTÉK_MEGY_MEGHALT_HŐS:
hős.sor(9);
hős.oszlop(0);
System.out.println("Megettek a(z) " + idő + ". lépésben!");
break;
case Labirintus.JÁTÉK_VÉGE_MINDEN_KINCS_MEGVAN:
System.out.println("Megvan minden kincs a(z) " + idő
+ ". lépésben!");
játékVége = true;
break;
case Labirintus.JÁTÉK_VÉGE_MEGHALT_HŐS:
System.out.println("Minden életem elfogyott a(z) " + idő
+ ". lépésben!");
játékVége = true;
break;
}
System.out.println("A labirintus a(z) " + idő + ". lépésben:");
labirintus.nyomtat(hős);
}
System.out.println("Megtalált értékek: " + hős.pontszám());
System.out.println("Játékidő: " + idő + " lépés");
System.out.println("Hányszor ettek meg: "
+ (Hős.ÉLETEK_SZÁMA - hős.életek()));
}
/** Megadja, hogy milyen gyorsan telik az idő a játékban. */
private void idoegyseg() {
++idő;
try {
Thread.sleep(1000);
} catch(InterruptedException e) {}
}
/**
* Átveszi a játék indításához szükséges paramétereket, majd
* elindítja a játék világának működését.
*
* @param args a labirintus tervét tartalmazó állomány neve az első
* parancssor-argumentum.
*/
public static void main(String[] args) {
if(args.length != 1) {
System.out.println("Indítás: java LabirintusVilág labirintus.txt");
System.exit(-1);
}
try {
new LabirintusVilág(args[0]);
} catch(RosszLabirintusKivétel rosszLabirintusKivétel) {
System.out.println(rosszLabirintusKivétel);
}
}
}
Page 198
Java esettanulmányok
170 Created by XMLmind XSL-FO Converter.
Minden esettanulmányra igaz lesz, hogy a játék időfejlődését egy külön szálba programozzuk be. Jelen esetben
ezt a szálat is a LabirintusVilág osztályban
public class LabirintusVilág implements Runnable {
implementáltuk, ennek megfelelően az időfejlődést a run() módszerbe helyeztük.
1.1.2.2. A tiszta OO labirintus fordítása, futtatása
C:\...\Munkakönyvtár> javac javattanitok\LabirintusVilág.java
C:\...\Munkakönyvtár>java javattanitok.LabirintusVilág
Indítás: java LabirintusVilág labirintus.txt
A program sikeres futtatásához még például az alábbi, A labirintust állományból felépítő konstruktor című
pontban megtárgyalt labirintus.txt állományt kell a Munkakönyvtár munkakönyvtárukba bemásolnunk.
//
// labirintus.txt
//
// DIGIT 2005, Javat tanítok
// Bátfai Norbert, [email protected]
//
// A labirintus szerkezetét megadó állomány, szerkezete a következő:
// a kincsek száma
// a szörnyek száma
// a labirintus szélessége
// magassága
// fal=1 járat=0 ...
// .
// .
// .
6
3
10
10
0 0 0 1 0 1 0 1 1 1
0 0 0 0 0 0 0 0 0 0
1 0 1 0 1 0 1 0 1 0
0 0 0 0 1 0 1 0 0 0
0 1 1 0 0 0 1 1 0 1
0 0 0 0 1 0 0 0 0 0
0 1 0 0 0 1 0 1 1 0
0 0 0 1 0 1 0 1 0 0
0 1 0 0 0 0 0 0 0 1
0 0 0 0 1 0 0 0 1 1
Ezután már bizonyosan sikeresen futtathatja a kedves Olvasó ezt a példát:
C:\...\Munkakönyvtár>java javattanitok.LabirintusVilág labirintus.txt
| | | |FAL| |FAL| |FAL|FAL|FAL
Page 199
Java esettanulmányok
171 Created by XMLmind XSL-FO Converter.
| | | | | | | | | |
|FAL| |FAL| |FAL| |FAL| |FAL|
| | | | |FAL| |FAL| | |
| |FAL|FAL| | | |FAL|FAL| |FAL
| | | | |FAL| | | | |
| |FAL| | | |FAL| |FAL|FAL|
| | | |FAL| |FAL| |FAL| |
| |FAL| | | | | | | |FAL
| | | | |FAL| | | |FAL|FAL
A labirintus a(z) 1. lépésben:
| | | |FAL| |FAL| |FAL|FAL|FAL
| | |S | | | | | | |
|FAL| |FAL| |FAL| |FAL| |FAL|
| | |K | |FAL| |FAL|S |K |
| |FAL|FAL| |SK | |FAL|FAL| |FAL
| | | | |FAL|K | |K | |
| |FAL|K | | |FAL| |FAL|FAL|
| | | |FAL| |FAL| |FAL| |
| |FAL| | | | | | | |FAL
| |H | | |FAL| | | |FAL|FAL
A labirintus a(z) 2. lépésben:
| | | |FAL| |FAL| |FAL|FAL|FAL
| | |S | | | | | | |
|FAL| |FAL| |FAL| |FAL| |FAL|
| | |K | |FAL| |FAL|S |K |
| |FAL|FAL| |SK | |FAL|FAL| |FAL
| | | | |FAL|K | |K | |
| |FAL|K | | |FAL| |FAL|FAL|
| | | |FAL| |FAL| |FAL| |
| |FAL| | | | | | | |FAL
| | |H | |FAL| | | |FAL|FAL
A labirintus a(z) 3. lépésben:
| | | |FAL| |FAL| |FAL|FAL|FAL
| | |S | | | | | | |
|FAL| |FAL| |FAL| |FAL| |FAL|
| | |K | |FAL| |FAL|S |K |
| |FAL|FAL| |SK | |FAL|FAL| |FAL
| | | | |FAL|K | |K | |
| |FAL|K | | |FAL| |FAL|FAL|
| | | |FAL| |FAL| |FAL| |
| |FAL| | | | | | | |FAL
| | | |H |FAL| | | |FAL|FAL
A labirintus a(z) 4. lépésben:
| | | |FAL| |FAL| |FAL|FAL|FAL
| | |S | | | | | | |
|FAL| |FAL| |FAL| |FAL| |FAL|
| | |K | |FAL| |FAL|S |K |
| |FAL|FAL| |SK | |FAL|FAL| |FAL
| | | | |FAL|K | |K | |
| |FAL|K | | |FAL| |FAL|FAL|
| | | |FAL| |FAL| |FAL| |
| |FAL| | | | | | | |FAL
| | | |H |FAL| | | |FAL|FAL
A labirintus a(z) 5. lépésben:
| | | |FAL| |FAL| |FAL|FAL|FAL
| | |S | | | | | | |
|FAL| |FAL| |FAL| |FAL| |FAL|
| | |K | |FAL| |FAL|S |K |
| |FAL|FAL| |SK | |FAL|FAL| |FAL
| | | | |FAL|K | |K | |
| |FAL|K | | |FAL| |FAL|FAL|
| | | |FAL| |FAL| |FAL| |
| |FAL| | | | | | | |FAL
| | | |H |FAL| | | |FAL|FAL
A labirintus a(z) 6. lépésben:
| | | |FAL| |FAL| |FAL|FAL|FAL
| | |S | | | | | | |
|FAL| |FAL| |FAL| |FAL| |FAL|
| | |K | |FAL| |FAL|S |K |
| |FAL|FAL| |SK | |FAL|FAL| |FAL
| | | | |FAL|K | |K | |
| |FAL|K | | |FAL| |FAL|FAL|
Page 200
Java esettanulmányok
172 Created by XMLmind XSL-FO Converter.
| | | |FAL| |FAL| |FAL| |
| |FAL| | | | | | | |FAL
| | | |H |FAL| | | |FAL|FAL
A labirintus a(z) 7. lépésben:
| | | |FAL| |FAL| |FAL|FAL|FAL
| | |S | | | | | | |
|FAL| |FAL| |FAL| |FAL| |FAL|
| | |K | |FAL| |FAL|S |K |
| |FAL|FAL| |SK | |FAL|FAL| |FAL
| | | | |FAL|K | |K | |
| |FAL|K | | |FAL| |FAL|FAL|
| | | |FAL| |FAL| |FAL| |
| |FAL| | | | | | | |FAL
| | | |H |FAL| | | |FAL|FAL
A labirintus a(z) 8. lépésben:
| | | |FAL| |FAL| |FAL|FAL|FAL
| | |S | | | | | | |
|FAL| |FAL| |FAL| |FAL| |FAL|
| | |K | |FAL| |FAL|S |K |
| |FAL|FAL| |SK | |FAL|FAL| |FAL
| | | | |FAL|K | |K | |
| |FAL|K | | |FAL| |FAL|FAL|
| | | |FAL| |FAL| |FAL| |
| |FAL| | | | | | | |FAL
| | | |H |FAL| | | |FAL|FAL
A labirintus a(z) 9. lépésben:
| | | |FAL| |FAL| |FAL|FAL|FAL
| | |S | | | | | | |
|FAL| |FAL| |FAL| |FAL| |FAL|
| | |K | |FAL| |FAL|S |K |
| |FAL|FAL| |SK | |FAL|FAL| |FAL
| | | | |FAL|K | |K | |
| |FAL|K | | |FAL| |FAL|FAL|
| | | |FAL| |FAL| |FAL| |
| |FAL| | | | | | | |FAL
| | | |H |FAL| | | |FAL|FAL
Megtalált értékek: 0
Játékidő: 10 lépés
Hányszor ettek meg: 0
1.1.3. A labirintus API és a LabirintusVilág a NetBeans IDE környezetben
A példa felélesztését most a NetBeans IDE fejlesztői környezetben is elvégezzük. Az a kedves Olvasó, aki eddig
esetleg még nem telepítette fel gépére a NetBeans IDE-t, A NetBeans integrált fejlesztői környezet használata
című pontban kaphat segítséget ehhez. A File/New Project... pont kiválasztása után a kinyíló New Project
ablakban első lépésként válasszuk a General kategóriát, azon belül a Java Application projektet.
A Next gombra következő ablakban elég a projekt nevét megadni, ez legyen mondjuk a JavatTanitokPeldak.
Ugyanitt még deaktiváljuk (szüntessük meg a kipipálását) a Create Main Class opciót.
Page 201
Java esettanulmányok
173 Created by XMLmind XSL-FO Converter.
A Finish gomb nyomásával a projekt létrehozását befejeztük.
A Source Packages/default package csomagon egy jobb egérgombot nyomva válasszuk a New/Java Package...
menüpontot! A kinyíló ablakban adjuk meg a javattanitok csomag nevet.
Page 202
Java esettanulmányok
174 Created by XMLmind XSL-FO Converter.
Miután a Finish gombbal befejezzük a létrehozást, a projekt forráskönyvtárában, esetünkben a C:\Documents
and Settings\norbi\JavatTanitokPeldak\src könyvtárban megjelenik a javattanitok alkönyvtár.
Másoljuk ide a kézikönyv LabirintusVilág.java forrását. Továbbá másoljuk ide a labirintus API-t, azaz a
labirintus könyvtárat, ami tartalmazza a korábban ismertetett Szereplő.java, Hős.java, Kincs.java,
Szörny.java, Labirintus.java, RosszLabirintusKivétel.java állományokat. Ha ezzel megvagyunk,
akkor a NetBeans IDE projektek füle a következőket kell mutassa:
Page 203
Java esettanulmányok
175 Created by XMLmind XSL-FO Converter.
A NetBeans IDE projektek fülében a JavatTanitokPeldak projekt néven egy jobb egérgombot nyomva a
tulajdonságok Properties menüpontot választva a kinyíló ablak Run pontját kérve a Main Class és az
Arguments mezőket kell beállítanunk.
Az Arguments mező értékének megfelelően a labirintus.txt állományt még be kell másolnunk a
C:\Documents and Settings\norbi\JavatTanitokPeldak könyvtárba. Ha ezzel megvagyunk, jöhet a
tesztelés, egyszerűen nyomjunk F6 funkcióbillentyűt, vagy a Run/Run Main Projekt menüpontot!
1.2. Java a játékokban: egy teljes képernyős példa - Labirintus Játék
A példát az Előzetes a példaprogramokból című pontban vezettük be, majd az előző esettanulmány A labirintus
API felélesztése című pontjában a labirintus API világát már életre keltettük, a jelen példában közelítünk a PC-s
játékokhoz, melyek tipikusan a teljes képernyőt uralják, a kifejlesztendő LabirintusJáték osztály a labirintus
mikrovilágának egy olyan életre keltését adja, ami hasonlóan képes erre, azaz a teljes képernyőn működik!
A példa kipróbálásához a LabirintusJáték osztályt a javattanitok könyvtárba kell bemásolnunk.
1.2.1. A LabirintusJáték osztály
Az osztály működése lényegében megegyezik az előző példáéval, de ránézésre bonyolultabbnak tűnhet, mivel itt
már grafikus felületen fut a program, ennek megfelelően valódi képeket - BufferedImage objektumokat -
használunk, amiket be kell tölteni, ki kell rajzolni. De annyiban valóban komplikáltabb a kód, hogy a nem
egyszerűen grafikus felületen, hanem teljes képernyős módban is dolgozik, ahol a képek kirajzolásának
hatékonyságát azzal is fokozzuk, hogy a kompatibilisKép függvénnyel a grafikus rendszerünkkel kompatibilis
formába is konvertáljuk őket. Továbbá a játékostól átvesszük a hőst mozgató billentyűzet inputot, amit a
KeyAdapter - névtelen belső, a konstruktorban definiált - adapter osztállyal kezelünk: az adott kurzor nyilaknak
megfelelő irányokba mozgatjuk a hőst, illetve az ESC billentyű nyomására kilépünk a programból.
/*
* LabirintusJáték.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
import javattanitok.labirintus.*;
/**
* A labirintus csomag absztrahálta labirintus mikrovilágának egy
* "teljes képernyős" (Full Screen Exclusive Mode API-s) életre
Page 204
Java esettanulmányok
176 Created by XMLmind XSL-FO Converter.
* keltésére ad példát ez az osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.LabirintusVilág
* @see javattanitok.LabirintusApplet
* @see javattanitok.LabirintusServlet
* @see javattanitok.LabirintusMIDlet
* @see javattanitok.HálózatiLabirintus
* @see javattanitok.TávoliLabirintus
* @see javattanitok.KorbásLabirintus
* @see javattanitok.ElosztottLabirintus
*/
public class LabirintusJáték extends java.awt.Frame
implements Runnable {
/** A labirintus. */
Labirintus labirintus;
/** A hős. */
Hős hős;
/** A játékbeli idő mérésére.*/
private long idő = 0;
/** Jelzi a játék végét, ezután a játék álapota már nem változik. */
private boolean játékVége = false;
/** A játék végén a játékost tájékoztató üzenet. */
String végeÜzenet = "Vége a játéknak!";
/** Jelzi, hogy a program terminálhat. */
private boolean játékKilép = false;
/** A labirintus szereplőihez rendelt képek. Ebben a példában már
* BufferedImage képeket használunk, mert majd a teljesítmény javitás
* apropóján ezeket a grafikus konfigurációnkhoz igazítjuk. */
java.awt.image.BufferedImage falKép;
java.awt.image.BufferedImage járatKép;
java.awt.image.BufferedImage hősKép;
java.awt.image.BufferedImage szörnyKép;
java.awt.image.BufferedImage kincsKép;
// A fullscreenbe kapcsoláshoz
java.awt.GraphicsDevice graphicsDevice;
// A megjelenítéshez
java.awt.image.BufferStrategy bufferStrategy;
/**
* A <code>LabirintusJáték</code> objektum elkészítése.
*
* @param labirintusFájlNév a labirintust definiáló, megfelelő
* szerkezetű szöveges állomány neve.
* @exception RosszLabirintusKivétel ha a labirintust definiáló állomány nem
* a megfelelő szerkezetű
*/
public LabirintusJáték(String labirintusFájlNév)
throws RosszLabirintusKivétel {
/* A labirintus felépítése. */
// A labirintus elkészítése állományból
labirintus = new Labirintus(labirintusFájlNév);
// A hős elkészítése és a kezdő pozíciójának beállítása
hős = new Hős(labirintus);
// A hős kezdő pozíciója
hős.sor(9);
hős.oszlop(0);
/* Teljes képernyős módba próbálunk váltani. */
// A lokális grafikus környezet elkérése
java.awt.GraphicsEnvironment graphicsEnvironment
= java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment();
// A grafikus környzetből a képernyővel dolgozunk
graphicsDevice = graphicsEnvironment.getDefaultScreenDevice();
// Próbálunk teljes képernyős, most speciálisan 1024x768-ba váltani
teljesKépernyősMód(graphicsDevice);
// Átadjuk a grafikus konfigurációt a kompatibilis képek elkészítéséhez
képErőforrásokBetöltése(graphicsDevice.getDefaultConfiguration());
// A hős mozgatása a KURZOR billenytűkkel, ESC kilép
addKeyListener(new java.awt.event.KeyAdapter() {
public void keyPressed(java.awt.event.KeyEvent billentyűEsemény) {
int billentyű = billentyűEsemény.getKeyCode();
Page 205
Java esettanulmányok
177 Created by XMLmind XSL-FO Converter.
if(!játékVége)
switch(billentyű) { // hős mozgatása
case java.awt.event.KeyEvent.VK_UP:
hős.lépFöl();
break;
case java.awt.event.KeyEvent.VK_DOWN:
hős.lépLe();
break;
case java.awt.event.KeyEvent.VK_RIGHT:
hős.lépJobbra();
break;
case java.awt.event.KeyEvent.VK_LEFT:
hős.lépBalra();
break;
}
// Kilépés a játékból
if(billentyű == java.awt.event.KeyEvent.VK_ESCAPE)
játékKilép = true;
// A játékban történt változások a képernyőn
// is jelenjenek meg
rajzolniKell();
};
});
// A játékbeli idő folyását biztosító szál elkészítése és indítása
new Thread(this).start();
}
/** A játék időbeli fejlődésének vezérlése. */
synchronized public void run() {
while(!játékKilép) {
// Aktív renderelés
rajzol();
idoegyseg();
switch(labirintus.bolyong(hős)) {
case Labirintus.JÁTÉK_MEGY_HŐS_RENDBEN:
break;
case Labirintus.JÁTÉK_MEGY_MEGHALT_HŐS:
// Még van élete, visszatesszük a kezdő pozícióra
hős.sor(9);
hős.oszlop(0);
break;
case Labirintus.JÁTÉK_VÉGE_MINDEN_KINCS_MEGVAN:
végeÜzenet = "Győztél, vége a játéknak!";
játékVége = true;
break;
case Labirintus.JÁTÉK_VÉGE_MEGHALT_HŐS:
végeÜzenet = "Vesztettél, vége a játéknak!";
játékVége = true;
break;
}
}
// Kilépés a játékból
setVisible(false);
graphicsDevice.setFullScreenWindow(null);
System.exit(0);
}
/**
* Ébresztő az várakozó rajzolást végző szálnak, ki kell rajzolni a játék
* grafikus felületét.
Page 206
Java esettanulmányok
178 Created by XMLmind XSL-FO Converter.
*/
synchronized public void rajzolniKell() {
notify();
}
/**
* Megadja, hogy milyen gyorsan telik az idő a játékban.
*/
private void idoegyseg() {
++ idő;
try {
wait(1000);
} catch (InterruptedException e) {}
}
/**
* Kép erőforrások betöltése.
*
* @param graphicsConfiguration a grafikus komfigurációval kompatibilis
* képek készítéséhez.
*/
public void képErőforrásokBetöltése(java.awt.GraphicsConfiguration
graphicsConfiguration) {
falKép = kompatibilisKép("fal.png", graphicsConfiguration);
járatKép = kompatibilisKép("járat.png", graphicsConfiguration);
hősKép = kompatibilisKép("hős.png", graphicsConfiguration);
szörnyKép = kompatibilisKép("szörny.png", graphicsConfiguration);
kincsKép = kompatibilisKép("kincs.png", graphicsConfiguration);
}
/**
* A grafikus konfigurációhoz igazítot kép.
*
* @param képNév a kép állomány neve
* @param graphicsConfiguration a grafikus komfigurációval kompatibilis
* képek készítéséhez.
*/
public java.awt.image.BufferedImage kompatibilisKép(String képNév,
java.awt.GraphicsConfiguration graphicsConfiguration) {
// Képet legegyszerűben a Swing-beli ImageIcon-al tölthetünk be:
java.awt.Image kép = new javax.swing.ImageIcon
(képNév).getImage();
// ebből BufferedImage-et készítünk, hogy hozzáférjünk a transzparencia
// értékhez (pl. a hős, a kincs és a szörny transzparens nálunk)
java.awt.image.BufferedImage bufferedImage =
new java.awt.image.BufferedImage(kép.getWidth(null),
kép.getHeight(null),
java.awt.image.BufferedImage.TYPE_INT_ARGB);
java.awt.Graphics2D g0 = bufferedImage.createGraphics();
g0.drawImage(kép, 0, 0, null);
g0.dispose();
// Az előző lépéshez hasonló lépésben most egy olyan BufferedImage-et,
// készítünk, ami kompatibilis a grafikus konfigurációnkkal
java.awt.image.BufferedImage kompatibilisKép
= graphicsConfiguration.createCompatibleImage(
bufferedImage.getWidth(), bufferedImage.getHeight(),
bufferedImage.getColorModel().getTransparency());
java.awt.Graphics2D g = kompatibilisKép.createGraphics();
g.drawImage(bufferedImage, 0, 0, null);
g.dispose();
return kompatibilisKép;
}
/**
Page 207
Java esettanulmányok
179 Created by XMLmind XSL-FO Converter.
* A játék grafikus felületének aktív renderelése.
*/
public void rajzol() {
java.awt.Graphics g = bufferStrategy.getDrawGraphics();
// A labirintus kirajzolása
for(int i=0; i<labirintus.szélesség(); ++i) {
for(int j=0; j<labirintus.magasság(); ++j) {
if(labirintus.szerkezet()[j][i])
g.drawImage(falKép, i*falKép.getWidth(),
j*falKép.getHeight(), null);
else
g.drawImage(járatKép, i*járatKép.getWidth(),
j*járatKép.getHeight(), null);
}
}
// A kincsek kirajzolása
Kincs[] kincsek = labirintus.kincsek();
for(int i=0; i<kincsek.length; ++i) {
g.drawImage(kincsKép,
kincsek[i].oszlop()*kincsKép.getWidth(),
kincsek[i].sor()*kincsKép.getHeight(), null);
// Ha már megvan a kics, akkor áthúzzuk
if(kincsek[i].megtalálva()) {
g.setColor(java.awt.Color.red);
g.drawLine(kincsek[i].oszlop()*kincsKép.getWidth(),
kincsek[i].sor()*kincsKép.getHeight(),
kincsek[i].oszlop()*kincsKép.getWidth()
+ kincsKép.getWidth(),
kincsek[i].sor()*kincsKép.getHeight()
+ kincsKép.getHeight());
g.drawLine(kincsek[i].oszlop()*kincsKép.getWidth()
+kincsKép.getWidth(),
kincsek[i].sor()*kincsKép.getHeight(),
kincsek[i].oszlop()*kincsKép.getWidth(),
kincsek[i].sor()*kincsKép.getHeight()
+ kincsKép.getHeight());
} else {
// ellenkező esetben kiírjuk az értékét
g.setColor(java.awt.Color.yellow);
g.drawString(""+kincsek[i].érték(),
kincsek[i].oszlop()*kincsKép.getWidth()
+ kincsKép.getWidth()/2,
kincsek[i].sor()*kincsKép.getHeight()
+ kincsKép.getHeight()/2);
}
}
// A szörnyek kirajzolása
Szörny[] szörnyek = labirintus.szörnyek();
for(int i=0; i<szörnyek.length; ++i)
g.drawImage(szörnyKép,
szörnyek[i].oszlop()*szörnyKép.getWidth(),
szörnyek[i].sor()*szörnyKép.getHeight(), null);
// A hős kirajzolása
g.drawImage(hősKép,
hős.oszlop()*hősKép.getWidth(),
hős.sor()*hősKép.getHeight(), null);
// A játék aktuális adataiból néhány kiíratása
g.setColor(java.awt.Color.black);
g.drawString("Életek száma: "+hős.életek(), 10, 40);
g.drawString("Gyűjtött érték: "+hős.pontszám(), 10, 60);
g.drawString("Idő: "+idő, 10, 80);
if(játékVége)
Page 208
Java esettanulmányok
180 Created by XMLmind XSL-FO Converter.
g.drawString(végeÜzenet, 420, 350);
g.dispose();
if (!bufferStrategy.contentsLost())
bufferStrategy.show();
}
/**
* Teljes képernyős módba (Full Screen Exclusive Mode) kapcsolás.
* Ha nem támogatott, akkor sima ablak fejléc és keret nélkül.
*/
public void teljesKépernyősMód(java.awt.GraphicsDevice graphicsDevice) {
int szélesség = 0;
int magasság = 0;
// Nincs ablak fejléc, keret.
setUndecorated(true);
// Mi magunk fogunk rajzolni.
setIgnoreRepaint(true);
// Nincs átméretezés
setResizable(false);
// Át tudunk kapcsolni fullscreenbe?
boolean fullScreenTamogatott = graphicsDevice.isFullScreenSupported();
// Ha tudunk, akkor Full-Screen exkluzív módba váltunk
if(fullScreenTamogatott) {
graphicsDevice.setFullScreenWindow(this);
// az aktuális képernyő jellemzök (szélesség, magasság, színmélység,
// frissítési frekvencia) becsomagolt elkérése
java.awt.DisplayMode displayMode
= graphicsDevice.getDisplayMode();
// és kiíratása
szélesség = displayMode.getWidth();
magasság = displayMode.getHeight();
int színMélység = displayMode.getBitDepth();
int frissítésiFrekvencia = displayMode.getRefreshRate();
System.out.println(szélesség
+ "x" + magasság
+ ", " + színMélység
+ ", " + frissítésiFrekvencia);
// A lehetséges képernyő beállítások elkérése
java.awt.DisplayMode[] displayModes
= graphicsDevice.getDisplayModes();
// Megnézzük, hogy támogatja-e az 1024x768-at, mert a
// példa játékunkhoz ehhez a felbontáshoz készítettük a képeket
boolean dm1024x768 = false;
for(int i=0; i<displayModes.length; ++i) {
if(displayModes[i].getWidth() == 1024
&& displayModes[i].getHeight() == 768
&& displayModes[i].getBitDepth() == színMélység
&& displayModes[i].getRefreshRate()
== frissítésiFrekvencia) {
graphicsDevice.setDisplayMode(displayModes[i]);
dm1024x768 = true;
break;
}
}
if(!dm1024x768)
System.out.println("Nem megy az 1024x768, de a példa képméretei ehhez a
felbontáshoz vannak állítva.");
} else {
setSize(szélesség, magasság);
validate();
setVisible(true);
}
createBufferStrategy(2);
bufferStrategy = getBufferStrategy();
Page 209
Java esettanulmányok
181 Created by XMLmind XSL-FO Converter.
}
/**
* Átveszi a játék indításához szükséges paramétereket, majd
* elindítja a játék világának működését.
*
* @param args a labirintus tervét tartalmazó állomány neve az első
* parancssor-argumentum.
*/
public static void main(String[] args) {
if(args.length != 1) {
System.out.println("Indítás: java LabirintusJáték labirintus.txt");
System.exit(-1);
}
try {
new LabirintusJáték(args[0]);
} catch(RosszLabirintusKivétel rosszLabirintusKivétel) {
System.out.println(rosszLabirintusKivétel);
}
}
}
Az osztály teljesKépernyősMód() függvénye végzi el a program teljes képernyős módba léptetését. Itt mi
konkrétan egy 1024x768 felbontású üzemmódba akarunk kapcsolni. Az Olvasó ezt a felbontást a saját
monitorjához tudja igazítani, illetve a a displayModes tömbbe lekérdezett lehetséges grafikus módok közül
választhat is.
1.2.2. A teljes képernyős labirintus fordítása, futtatása
A program sikeres futtatásához az alábbi, egyenként 102x76 pixel méretű képeket
a fal.png, a járat.png, a hős.png, a kincs.png és a szörny.png állományokat kell a Munkakönyvtár
munkakönyvtárukba bemásolnunk. (Ezek a képek A példaprogramok forrásainak letöltése című pontban
ismertetett archívumban is megtalálhatóak.) E képállományok elkészítése után már bizonyosan sikeresen
futtathatja a kedves Olvasó ezt a példát is:
C:\...\Munkakönyvtár> javac javattanitok\LabirintusJáték.java
C:\...\Munkakönyvtár> java javattanitok.LabirintusJáték labirintus.txt
Page 210
Java esettanulmányok
182 Created by XMLmind XSL-FO Converter.
1.3. Java a böngészőkben: Applet objektumok - Labirintus Applet
A példát az Előzetes a példaprogramokból című pontban vezettük be.
1.3.1. A LabirintusApplet osztály
Ennek az osztálynak a lényegi működése az előző kettővel, funkcionális működése pedig az előző osztályéval
egyezik meg. A grafikus felület kezelését nagyban egyszerűsíti, hogy nem teljes képernyős módban dolgozunk.
Látszólag viszont bonyolítja, hogy az osztály letölthető appletként és különálló alkalmazásként is kész a futásra.
Ha a programot a böngésző tölti le, azaz appletként fog futni, akkor nem a main(), hanem az init() függvénye
végrehajtásával indul, azaz miután a böngésző példányosított a LabirintusApplet osztályból egy objektumot,
akkor annak init() módszere hívódik. Ez esetben tehát nem mi magunk példányosítottunk az appletből.
/*
* LabirintusApplet.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
import javattanitok.labirintus.*;
/**
* A labirintus csomag absztrahálta labirintus mikrovilágának egy
* appletbeli életre keltésére ad példát ez az osztály. Ennek megfelelően
* appletként a böngészőben, alkalmazásként külön ablakban történő
* megjelenítést biztosít.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.LabirintusVilág
* @see javattanitok.LabirintusJáték
* @see javattanitok.LabirintusMIDlet
* @see javattanitok.LabirintusServlet
Page 211
Java esettanulmányok
183 Created by XMLmind XSL-FO Converter.
* @see javattanitok.HálózatiLabirintus
* @see javattanitok.TávoliLabirintus
* @see javattanitok.KorbásLabirintus
* @see javattanitok.ElosztottLabirintus
*/
public class LabirintusApplet extends java.applet.Applet
implements java.awt.event.KeyListener {
/** A labirintus. */
Labirintus labirintus;
/** A hős. */
Hős hős;
/** A játék vége után már nem veszünk át inputot a játékostól,
* illetve a játék világának állapota sem változik. */
boolean játékVége = false;
/** A játék vége után megjelenő üzenet. */
String végeÜzenet = "Vége a játéknak!";
// Ha nam appletként indítjuk a programot, hanem alkalmazásként, akkor
// ez lesz az alkalmazás ablaka
java.awt.Frame ablak;
// A labirintus szereplőihez rendelt képek
java.awt.Image falKép;
java.awt.Image járatKép;
java.awt.Image hősKép;
java.awt.Image szörnyKép;
java.awt.Image kincsKép;
/**
* Az applet életciklusának indítása, csak akkor fut le, ha appletként
* indítotuk a programot.
*/
public void init() {
addKeyListener(this);
indul(true);
}
/**
* Akár appletként, akár alkalmazásként indítjuk a programot, itt
* végezzük el az inicializálás javát.
*/
public void indul(boolean appletként) {
ClassLoader classLoader = this.getClass().getClassLoader();
falKép = new javax.swing.ImageIcon
(classLoader.getResource("fal.png")).getImage();
járatKép = new javax.swing.ImageIcon
(classLoader.getResource("járat.png")).getImage();
hősKép = new javax.swing.ImageIcon
(classLoader.getResource("hős.png")).getImage();
szörnyKép = new javax.swing.ImageIcon
(classLoader.getResource("szörny.png")).getImage();
kincsKép = new javax.swing.ImageIcon
(classLoader.getResource("kincs.png")).getImage();
labirintus = new Labirintus(6, 3);
hős = new Hős(labirintus);
hős.sor(9);
hős.oszlop(0);
// ha nem appletként indítottuk a programot
if(!appletként) {
// akkor nyitunk neki egy ablakot
ablak = new java.awt.Frame("Labirintus applet alkalmazásként");
// amit be is lehet csukni
ablak.addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
ablak.setVisible(false);
System.exit(0);
}
});
ablak.add(this);
ablak.addKeyListener(this);
Page 212
Java esettanulmányok
184 Created by XMLmind XSL-FO Converter.
ablak.setSize(1024, 768);
ablak.setVisible(true);
}
}
/**
* A játékostól (aki a játék világában a hős) jövő input feldolgozása:
* a hős mozgatása a KURZOR billenytűkkel, illetve a játék világának
* állapot változásait is innen irányítjuk.
*/
public void keyPressed(java.awt.event.KeyEvent billentyűEsemény) {
// Mit nyomott le?
int billentyű = billentyűEsemény.getKeyCode();
if(!játékVége) {
// Merre lép a hős?
switch(billentyű) {
// A KURZOR billentyűkkel foglalkozunk, a megfelelő irányba
// lépünk
case java.awt.event.KeyEvent.VK_UP:
hős.lépFöl();
break;
case java.awt.event.KeyEvent.VK_DOWN:
hős.lépLe();
break;
case java.awt.event.KeyEvent.VK_RIGHT:
hős.lépJobbra();
break;
case java.awt.event.KeyEvent.VK_LEFT:
hős.lépBalra();
break;
}
// A játék világának állapot változása: azaz a játék többi
// szereplője is lép. Ha ezzel a lépéssel a játék világában
// történt valami lényeges: pl. vége a játéknak vagy egy szörny
// elkapta a hőst, akkor reagálunk:
switch(labirintus.bolyong(hős)) {
case Labirintus.JÁTÉK_MEGY_HŐS_RENDBEN:
break;
case Labirintus.JÁTÉK_MEGY_MEGHALT_HŐS:
// Még van élete, visszatesszük a kezdő pozícióra
hős.sor(9);
hős.oszlop(0);
break;
case Labirintus.JÁTÉK_VÉGE_MINDEN_KINCS_MEGVAN:
végeÜzenet = "Győztél, vége a játéknak!";
játékVége = true;
break;
case Labirintus.JÁTÉK_VÉGE_MEGHALT_HŐS:
végeÜzenet = "Vesztettél, vége a játéknak!";
játékVége = true;
break;
}
// Amíg nincs vége a játéknak, újra rajzoljuk a
// játék felületét, hogy látszódjanak a játék állapotában
// bekövetkezett változások
repaint();
}
}
/**
* A KeyListener számunkra most érdektelen további metódusait üres
* testtel definiáljuk felül.
*/
public void keyTyped(java.awt.event.KeyEvent billentyűEsemény) {}
public void keyReleased(java.awt.event.KeyEvent billentyűEsemény) {}
/**
* Kajzolja a játék felületét, azaz a labirintust és a benne szereplőket:
* a hőst, a kincseket és a szörnyeket.
*/
Page 213
Java esettanulmányok
185 Created by XMLmind XSL-FO Converter.
public void paint(java.awt.Graphics g) {
// A labirintus kirajzolása
for(int i=0; i<labirintus.szélesség(); ++i) {
for(int j=0; j<labirintus.magasság(); ++j) {
if(labirintus.szerkezet()[j][i])
g.drawImage(falKép, i*falKép.getWidth(this),
j*falKép.getHeight(this), null);
else
g.drawImage(járatKép, i*járatKép.getWidth(this),
j*járatKép.getHeight(this), null);
}
}
// A kincsek kirajzolása
Kincs[] kincsek = labirintus.kincsek();
for(int i=0; i<kincsek.length; ++i) {
g.drawImage(kincsKép,
kincsek[i].oszlop()*kincsKép.getWidth(this),
kincsek[i].sor()*kincsKép.getHeight(this), null);
// Ha már megvan a kics, akkor áthúzzuk
if(kincsek[i].megtalálva()) {
g.setColor(java.awt.Color.red);
g.drawLine(kincsek[i].oszlop()*kincsKép.getWidth(this),
kincsek[i].sor()*kincsKép.getHeight(this),
kincsek[i].oszlop()*kincsKép.getWidth(this)
+ kincsKép.getWidth(this),
kincsek[i].sor()*kincsKép.getHeight(this)
+ kincsKép.getHeight(this));
g.drawLine(kincsek[i].oszlop()*kincsKép.getWidth(this)
+ kincsKép.getWidth(this),
kincsek[i].sor()*kincsKép.getHeight(this),
kincsek[i].oszlop()*kincsKép.getWidth(this),
kincsek[i].sor()*kincsKép.getHeight(this)
+ kincsKép.getHeight(this));
} else {
// ellenkező esetben kiírjuk az értékét
g.setColor(java.awt.Color.yellow);
g.drawString(""+kincsek[i].érték(),
kincsek[i].oszlop()*kincsKép.getWidth(this)
+ kincsKép.getWidth(this)/2,
kincsek[i].sor()*kincsKép.getHeight(this)
+ kincsKép.getHeight(this)/2);
}
}
// A szörnyek kirajzolása
Szörny[] szörnyek = labirintus.szörnyek();
for(int i=0; i<szörnyek.length; ++i)
g.drawImage(szörnyKép,
szörnyek[i].oszlop()*szörnyKép.getWidth(this),
szörnyek[i].sor()*szörnyKép.getHeight(this), null);
// A hős kirajzolása
g.drawImage(hősKép,
hős.oszlop()*hősKép.getWidth(this),
hős.sor()*hősKép.getHeight(this), null);
// A játék aktuális adataiból néhány kiíratása
g.setColor(java.awt.Color.black);
g.drawString("Életek száma: "+hős.életek(), 10, 40);
g.drawString("Gyűjtött érték: "+hős.pontszám(), 10, 60);
if(játékVége) {
g.setColor(java.awt.Color.black);
g.drawString(végeÜzenet, 420, 350);
}
}
Page 214
Java esettanulmányok
186 Created by XMLmind XSL-FO Converter.
/**
* A játék felületének kirajzolásakor ne legyen villogás, ezért
* az eredeti, a felület törlését elvégző update metódust felüldefiniáljuk.
*/
public void update(java.awt.Graphics g) {
paint(g);
}
/**
* A program alkalmazásként való indíthatóságát szolgálja.
*/
public static void main(String[] args) {
// Ha itt van a vezérlés, akkor nem igaz az, hogy appletként indítottuk
new LabirintusApplet().indul(false);
}
}
1.3.2. A labirintus applet fordítása, futtatása
A LabirintusApplet osztályt fordítása után
C:\...\Munkakönyvtár> javac javattanitok\LabirintusApplet.java
alkalmazásként is futtathatjuk:
C:\...\Munkakönyvtár> java javattanitok.LabirintusApplet
de izgalmasabb hozzá egy HTML dokumentumot készíteni, mondjuk a labirintus.html néven a
Munkakönyvtár nevű munkakönyvtárunkban az alábbi tartalommal.
<applet code="javattanitok/LabirintusApplet.class"
width="1024" height="768">
</applet>
Az appletet ezután az appletek tesztelésére használható, a JDK-beli appletviewer parancsot használva
tesztelhetjük:
C:\...\Munkakönyvtár> appletviewer labirintus.html
S a legizgalmasabb magában a böngészőben való kipróbálás, ehhez futtassuk a szintén a JDK részeként kapott
HtmlConverter.exe nevű programot a JDK bin könyvtárából, ahol a kinyíló Swinges ablakban a Specify a file
or a directory path: szövegmezőbe böngésszük be a C:\Documents and
Settings\norbi\Dokumentumok\Javat_tanitok\Munkakönyvtár munkakönyvtárunkat, majd végezzük el a
konvertálást, ami a labirintus.html állományunkat a következőre alakítja át:
<!--"CONVERTED_APPLET"-->
<!-- HTML CONVERTER -->
Page 215
Java esettanulmányok
187 Created by XMLmind XSL-FO Converter.
<object
classid = "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"
codebase = "http://java.sun.com/update/1.6.0/jinstall-1_6-windows-
i586.cab#Version=6,0,0,86"
WIDTH = "1024" HEIGHT = "768" >
<PARAM NAME = CODE VALUE = "javattanitok/LabirintusApplet.class" >
<param name = "type" value = "application/x-java-applet;version=1.6">
<param name = "scriptable" value = "false">
<comment>
<embed
type = "application/x-java-applet;version=1.6" \
CODE = "javattanitok/LabirintusApplet.class" \
WIDTH = "1024" \
HEIGHT = "768"
scriptable = false
pluginspage = "http://java.sun.com/products/plugin/index.html#download">
<noembed>
</noembed>
</embed>
</comment>
</object>
<!--
<APPLET CODE = "javattanitok/LabirintusApplet.class" WIDTH = "1024" HEIGHT = "768">
</APPLET>
-->
<!--"END_CONVERTED_APPLET"-->
majd ezt a konvertálás után kapott állományt nyissuk ki a böngészőnkkel:
Page 216
Java esettanulmányok
188 Created by XMLmind XSL-FO Converter.
1.4. Java a mobiltelefonokban: MIDlet objektumok - Labirintus MIDlet
Az Előzetes a példaprogramokból című pontban bevezetett esettanulmány végleges kódját adjuk meg itt.
A példa fejlesztését a NetBeans IDE környezetben végezzük. A File/New Project... pont kiválasztása után a
kinyíló New Project ablakban első lépésként válaszuk a Mobile kategóriát, azon belül a Mobil Application
projektet. A Next gombra következő ablakban elég a projekt nevét megadni, ez legyen mondjuk a
JavatTanitokMobilLabirintus. Ugyanitt még deaktiváljuk (szüntessük meg a kipipálását) a Create Hello
MIDlet opciót. Majd a felkínált alapértelmezések elfogadása mellett nyomjunk újabb Next gombokat a Finish
gomb megjelenéséig, melynek nyomásával a projekt létrehozását befejeztük.
A default package csomagon egy jobb egérgombot nyomva válasszuk a New/Java Package... menüpontot! A
kinyíló ablakban adjuk meg a javattanitok csomag nevet. A jobb egérgomb újbóli nyomásával válasszuk a
New/MIDlet... menüpontot! A kinyíló ablakban adjuk meg a nevét - ez tetszőleges lehet. Viszont a MIDP Class
Name megadásánál nagyon gondosan járjunk el, egészen pontosan azt a nevet adjuk meg (kis-nagybetű
helyesen) amely osztály azt mondja magáról, hogy extends MIDlet, esetünkben ez az osztály a
LabirintusMIDlet osztály. Miután a Finish gombbal befejezzük a létrehozást, a projekt forráskönyvtárában,
esetünkben a C:\Documents and Settings\norbi\JavatTanitokMobilLabirintus\src\javattanitok
könyvtárban megjelenik a LabirintusMIDlet.java forrásállomány. Most annyit tegyünk még, hogy ezt az
állomány írjuk felül a kézikönyv alább bemutatott LabirintusMIDlet.java állományával és e mellé másoljuk
még be a kézikönyv, szintén a következőkben bemutatott LabirintusVaszon.java forrását. Továbbá másoljuk
ide a labirintus API-t, azaz a labirintus könyvtárat, ami tartalmazza a korábban ismertetett Szereplő.java,
Hős.java, Kincs.java, Szörny.java, Labirintus.java, RosszLabirintusKivétel.java állományokat.
Ha ezzel megvagyunk, akkor a NetBeans IDE projektek füle a következőket kell mutassa:
Page 217
Java esettanulmányok
189 Created by XMLmind XSL-FO Converter.
Sajnos itt a labirintus API ékezetes osztálynevei problémát okoznak a projekt fordítása után, így ezeket, azaz a
Szereplő.java, Hős.java, Szörny.java, RosszLabirintusKivétel.java állományokat átírjuk ékezet
nélküli nevekre. Mielőtt belekezdenénk ebbe a sziszifuszi munkába, van királyi út is: nyomjunk jobb
egérgombot ezeken a neveken a projektek fülben, majd a Refactor/Rename... menüpont választása után kinyíló
ablakban adjuk meg a megfelelő ékezet nélküli osztálynevet. A Next, majd a Do Refactoring gombokat
nyomva az átnevezés (és minden hivatkozás átnevezése) kész. Ha ezzel is megvagyunk, akkor a NetBeans IDE
projektek füle a következőket kell mutassa:
Page 218
Java esettanulmányok
190 Created by XMLmind XSL-FO Converter.
Már majdnem készen állunk a projekt fordítására és futtatására. Annyi van hátra, hogy a Labirintus osztály
tartalmaz olyan osztályokat, melyek csak a Java SE részei, így ezeket a jelen Java ME platformbeli munkánk
során módosítanunk kell! A legegyszerűbb eljárást választjuk, ezeket töröljük, egészen pontosan kommentbe
tesszük a Labirintus.java állományban az állományból építő konstruktort, mert ebben számos, csak a Java
SE-ben létező objektum, például a java.io.BufferedReader, java.util.StringTokenizer,
java.io.FileNotFoundException vagy a java.io.FileReader van.
public Labirintus(String labirintusFájlNév) throws RosszLabirintusKivetel {
Továbbá ugyancsak kommentezzük a java.io.PrintWriter nem Java ME osztály használatából bekövetkező,
hasonló problémát okozó
public void nyomtat(Hos hős, java.io.PrintWriter csatorna) {
függvényt.
Ezzel elkészültünk, jöhet a tesztelés, egyszerűen nyomjunk F6 funkcióbillentyűt, vagy a Run/Run Main Projekt
menüpontot!
Page 219
Java esettanulmányok
191 Created by XMLmind XSL-FO Converter.
A Launch-nek megfelelő telefon szoftbillentyűre kattintva elindul a mobil labirintus játékunk.
Page 220
Java esettanulmányok
192 Created by XMLmind XSL-FO Converter.
Még a játék képeit tartalmazó png állományokat kell megadnunk a projektnek, másoljuk be a hos.png,
kincs.png és a szorny.png állományokat például a C:\Documents and
Settings\norbi\JavatTanitokMobilLabirintus\res könyvtárba, majd a NetBeans IDE projektek fülében
a JavatTanitokMobilLabirintus néven egy jobb egérgombot nyomva a tulajdonságok Properties
menüpontot választva a kinyíló ablak Libraries & Resources pontját kérve, az Add Folder... gombbal a
C:\Documents and Settings\norbi\JavatTanitokMobilLabirintus\res könyvtárat hozzáadva immár,
ahogyan a következő kép is mutatja, a képeinket is elérjük.
1.4.1. A LabirintusMIDlet osztály
A LabirintusMIDlet osztályból a mobiltelefon példányosít, a konstruktor lefutása után meghívódik a
startApp() indító életciklus függvény. A MIDlet életciklussal a MIDlet feladat című pontban foglalkoztunk.
A LabirintusMIDlet osztály eseménykezelését pedig az Eseménykezelés című pontban tárgyaltuk.
/*
* LabirintusMIDlet.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
/**
* A labirintus csomag absztrahálta labirintus mikrovilágának egy
* mobiltelefonos életre keltésére ad példát ez az osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.LabirintusVilág
* @see javattanitok.LabirintusApplet
* @see javattanitok.LabirintusJáték
Page 221
Java esettanulmányok
193 Created by XMLmind XSL-FO Converter.
* @see javattanitok.LabirintusServlet
* @see javattanitok.HálózatiLabirintus
* @see javattanitok.TávoliLabirintus
* @see javattanitok.KorbásLabirintus
* @see javattanitok.ElosztottLabirintus
* @see javattanitok.LabirintusVaszon
*/
public class LabirintusMIDlet extends javax.microedition.midlet.MIDlet
implements javax.microedition.lcdui.CommandListener {
/** A MIDlethez tartozó kijelző. */
private javax.microedition.lcdui.Display kijelző;
/** Parancs a kilépéshez. */
private javax.microedition.lcdui.Command kilépésParancs;
/** A labirintus életre keltése és megjelenítése. */
private LabirintusVaszon labirintusVászon;
/**
* A <code>LabirintusMIDlet</code> objektum elkészítése.
*/
public LabirintusMIDlet() {
// A MIDlethez tartozó kijelző elkérése
kijelző = javax.microedition.lcdui.Display.getDisplay(this);
// A labirintus elkészítése
labirintusVászon = new LabirintusVaszon();
// A kilépés parancs elkészítése
kilépésParancs = new javax.microedition.lcdui.Command("Kilép",
javax.microedition.lcdui.Command.EXIT, 1);
// és a labirintus vászonra helyezése
labirintusVászon.addCommand(kilépésParancs);
// az eseményeket (most kilépés parancs) itt dolgozzuk fel
labirintusVászon.setCommandListener(this);
}
/** A MIDletet indító életciklus metódus. */
public void startApp() {
// A kijelzőn a labirintus vászon legyen látható
kijelző.setCurrent(labirintusVászon);
}
/**
* A MIDletet felfüggesztő életciklus metódus, azaz mit tegyünk,
* ha egy bejövő hívás vagy SMS megzavarja a programunk futását?
* (Most semmit, mert csupán üres testes implementációját adtuk a
* függvénynek.)
*/
public void pauseApp() {
}
/**
* A MIDletet befejező életciklus metódus, azaz mit tegyünk,
* ha programunk befejezi futását? (Most semmit, mert csupán
* üres testes implementációját adtuk a függvénynek.)
*/
public void destroyApp(boolean unconditional) {
// Leállítjuk a labirintus játék szálát
if(labirintusVászon != null)
labirintusVászon.játékKilép();
}
/**
* A labirintus játék parancsainak (jelen esetben egy ilyen van,
* a kilépés) kezelése.
*
* @param command parancs, ami keletkezett
* @param displayable valamelyik képernyőn
*/
public void commandAction(javax.microedition.lcdui.Command parancs,
javax.microedition.lcdui.Displayable képernyő) {
if (képernyő == labirintusVászon) {
if (parancs == kilépésParancs) {
// Leállítjuk a labirintus játék szálát
labirintusVászon.játékKilép();
// Leállítjuk a programot
kijelző.setCurrent(null);
destroyApp(true);
notifyDestroyed();
Page 222
Java esettanulmányok
194 Created by XMLmind XSL-FO Converter.
}
}
}
}
1.4.2. A LabirintusVaszon osztály
Mobil környezetben a GUI felépítésére - ahogyan a Bepillantás a GUI programozásba című pontban említettük
- a javax.microedition.lcdui csomag szolgál. A MIDlet objektumunkhoz tartozó kijelzőt már az iménti
MIDlet osztályunk konstruktorában elkértük a csomag Display osztályának statikus getDisplay()
függvényével, majd a MIDlet indulásakor
/** A MIDletet indító életciklus metódus. */
public void startApp() {
// A kijelzőn a labirintus vászon legyen látható
kijelző.setCurrent(labirintusVászon);
}
egy azt a LabirintusVaszon osztálybeli vásznunkat tettünk a kijelzőre, aminek a következő teljes kódját
közöljük. Vásznunk egy javax.microedition.lcdui.game csomagbeli GameCanvas. Az ilyen játék-vásznak
egyik előnye, hogy könnyen teljes kijelzős módba kapcsolhatók, amikor is a kijelzőn a szoftbillentyűknek
fenntartott hellyel is a programozó rendelkezhet és főleg, hogy ezen a vásznon könnyű a szálkezelést
összeegyeztetni a játék állapotváltozásaival és a vászon kirajzolásával. Mert még a klasszikus vászon használata
esetén felül kell definiálnunk a vászon paint() módszerét és ezt a program vezérlő szálából ciklikusan implicit
hívogatnunk a repaint() függvény hívásával, addig itt mindent: az eseménykezelést, a játék állapotának
változását és az ennek a változásnak megfelelő kirajzolást is egy helyen végezhetjük, a kódunkban is jól látható
run() módszerünkben.
/*
* LabirintusVaszon.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
import javattanitok.labirintus.*;
/**
* A labirintus csomag absztrahálta labirintus mikrovilágának egy
* mobiltelefonos életre keltésére ad példát ez az osztály: elkészíti,
* vezérli és megjeleníti a labirintust.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.LabirintusMIDlet
*/
public class LabirintusVaszon extends javax.microedition.lcdui.game.GameCanvas
implements Runnable {
/** A vászon szélessége. */
private int szélesség;
/** A vászon magassága. */
private int magasság;
/** A labirintus. */
Labirintus labirintus;
/** A hős. */
Hos hős;
/** A játékbeli idő folyását biztosító szál. */
Page 223
Java esettanulmányok
195 Created by XMLmind XSL-FO Converter.
private Thread játékSzál;
/** A játékbeli idő mérésére.*/
private long idő = 0;
/** Jelzi a játék végét, ezután a játék álapota már nem változik. */
private boolean játékVége = false;
/** A játék végén a játékost tájékoztató üzenet. */
String végeÜzenet = "Vége a játéknak!";
/** Jelzi, hogy a program terminálhat. */
private boolean játékKilép = false;
/** A labirintus egy fal vagy járat cellájának szélessége. */
private int téglaSzélesség;
/** A labirintus egy fal vagy járat cellájának magassága. */
private int téglaMagasság;
/** A szereplőkhöz rendelt képek. */
javax.microedition.lcdui.Image hősKép, szörnyKép, kincsKép;
/**
* A <code>LabirintusVászon</code> objektum elkészítése.
*/
public LabirintusVaszon() {
super(false);
// A mobil kijelzője teljes képernyős módba
setFullScreenMode(true);
// Milyenek ekkor a méretek?
szélesség = getWidth();
magasság = getHeight();
// A labirintus elkészítése
labirintus = new Labirintus(6, 3);
hős = new Hos(labirintus);
hős.sor(9);
hős.oszlop(0);
// A labirintusnak a telefon kijelző méretéhez igazítása
téglaSzélesség = szélesség/labirintus.szélesség();
téglaMagasság = magasság/labirintus.magasság();
try {
// A szereplőkhöz rendelt képek betöltése
hősKép =
javax.microedition.lcdui.Image.createImage("/hos.png");
kincsKép =
javax.microedition.lcdui.Image.createImage("/kincs.png");
szörnyKép =
javax.microedition.lcdui.Image.createImage("/szorny.png");
} catch(Exception e) {
hősKép = null;
kincsKép = null;
szörnyKép = null;
}
// A játékbeli idő folyását biztosító szál elkészítése
játékSzál = new Thread(this);
// és indítása
játékSzál.start();
}
/** A játék időbeli fejlődésének vezérlése. */
public void run() {
javax.microedition.lcdui.Graphics g = getGraphics();
while(!játékKilép) {
if(!játékVége) { // Ha még nincs vége, akkor érdemben
// reagálunk a billentyűzet lenyomásokra
int billentyű = getKeyStates();
// A kurzor gomboknak megfelelő irányba lépéssel
if ((billentyű & LEFT_PRESSED) != 0) {
hős.lépBalra();
} else if ((billentyű & RIGHT_PRESSED) != 0) {
hős.lépJobbra();
} else if ((billentyű & UP_PRESSED) != 0) {
Page 224
Java esettanulmányok
196 Created by XMLmind XSL-FO Converter.
hős.lépFöl();
} else if ((billentyű & DOWN_PRESSED) != 0) {
hős.lépLe();
}
}
switch(labirintus.bolyong(hős)) {
case Labirintus.JÁTÉK_MEGY_HŐS_RENDBEN:
break;
case Labirintus.JÁTÉK_MEGY_MEGHALT_HŐS:
// Még van élete, visszatesszük a kezdő pozícióra
hős.sor(9);
hős.oszlop(0);
break;
case Labirintus.JÁTÉK_VÉGE_MINDEN_KINCS_MEGVAN:
végeÜzenet = "Győztél, vége a játéknak!";
játékVége = true;
break;
case Labirintus.JÁTÉK_VÉGE_MEGHALT_HŐS:
végeÜzenet = "Vesztettél, vége a játéknak!";
játékVége = true;
break;
}
// A kijelző törlése
g.setColor(0x00FFFFFF);
g.fillRect(0, 0, getWidth(), getHeight());
// A labirintus kirajzolása
g.setColor(0x00ed7703);
for(int i=0; i<labirintus.szélesség(); ++i)
for(int j=0; j<labirintus.magasság(); ++j)
if(labirintus.szerkezet()[j][i])
g.fillRect(i*téglaSzélesség, j*téglaMagasság,
téglaSzélesség, téglaMagasság);
// A kincsek kirajzolása
Kincs[] kincsek = labirintus.kincsek();
for(int i=0; i<kincsek.length; ++i) {
if(kincsKép != null) {
if(!kincsek[i].megtalálva())
g.drawImage(kincsKép,
kincsek[i].oszlop()*téglaSzélesség,
kincsek[i].sor()*téglaMagasság,
javax.microedition.lcdui.Graphics.LEFT
|javax.microedition.lcdui.Graphics.TOP);
} else {
// Ha már megvan a kics, akkor szürkébbel rajzoljuk
if(kincsek[i].megtalálva())
g.setColor(0x00d2cfb7);
else // Különben sárgábbal
g.setColor(0x00fbe101);
g.fillRect(kincsek[i].oszlop()*téglaSzélesség,
kincsek[i].sor()*téglaMagasság,
téglaSzélesség/2, téglaMagasság);
}
}
// A szörnyek kirajzolása
g.setColor(0x00ff0000);
Szorny[] szörnyek = labirintus.szörnyek();
for(int i=0; i<szörnyek.length; ++i)
if(szörnyKép != null)
g.drawImage(szörnyKép,
szörnyek[i].oszlop()*téglaSzélesség,
szörnyek[i].sor()*téglaMagasság,
javax.microedition.lcdui.Graphics.LEFT
|javax.microedition.lcdui.Graphics.TOP);
else
g.fillRect(szörnyek[i].oszlop()*téglaSzélesség
+ téglaSzélesség/2,
szörnyek[i].sor()*téglaMagasság,
Page 225
Java esettanulmányok
197 Created by XMLmind XSL-FO Converter.
téglaSzélesség/2, téglaMagasság);
// A hős kirajzolása
if(hősKép != null)
g.drawImage(hősKép,
hős.oszlop()*téglaSzélesség,
hős.sor()*téglaMagasság,
javax.microedition.lcdui.Graphics.LEFT
|javax.microedition.lcdui.Graphics.TOP);
else {
g.setColor(0x0000ff00);
g.drawRect(hős.oszlop()*téglaSzélesség,
hős.sor()*téglaMagasság,
téglaSzélesség, téglaMagasság);
}
// A játék aktuális adataiból néhány kiíratása
g.setColor(0x000000ff);
g.drawString("Életek száma: "+hős.életek(), 10, 15,
javax.microedition.lcdui.Graphics.LEFT
|javax.microedition.lcdui.Graphics.BOTTOM);
g.drawString("Gyűjtött érték: "+hős.pontszám(), 10, 30,
javax.microedition.lcdui.Graphics.LEFT
|javax.microedition.lcdui.Graphics.BOTTOM);
g.drawString("Idő: "+idő/5, 10, 45,
javax.microedition.lcdui.Graphics.LEFT
|javax.microedition.lcdui.Graphics.BOTTOM);
if(játékVége)
g.drawString(végeÜzenet, 10, magasság-20,
javax.microedition.lcdui.Graphics.LEFT
|javax.microedition.lcdui.Graphics.BOTTOM);
flushGraphics();
idoegyseg();
}
}
/** A játék szál leállítása. */
public void játékKilép() {
játékKilép = true;
játékSzál = null;
}
/** Megadja, hogy milyen gyorsan telik az idő a játékban. */
private void idoegyseg() {
++ idő;
try {
Thread.sleep(200);
} catch(InterruptedException e) {}
}
}
A [PROGRAMOZÓ PÁTERNOSZTER JEGYZET] jegyzetben számos további mobil programozási példát talál
az érdeklődő Olvasó. Jelen vászon osztályunk triviális továbbfejlesztési lehetősége, hogy a szereplőket nem
képpel, hanem a javax.microedition.lcdui.game csomagbeli Sprite objektumokkal reprezentálnánk.
Ebben a feladatban is segít az imént hivatkozott jegyzet.
1.5. Java a webszerverekben: Servlet objektumok - Labirintus Servlet
Az Előzetes a példaprogramokból című pontban bevezetett esettanulmány végleges kódját adjuk meg itt.
Ennek a példának a fejlesztését is a NetBeans IDE fejlesztői környezetben végezzük. A File/New Project... pont
kiválasztása után a kinyíló New Project ablakban első lépésként válasszuk a Web kategóriát, azon belül a Web
Application projektet. A Next gombra következő ablakban elég a projekt nevét megadni, ez legyen mondjuk a
Page 226
Java esettanulmányok
198 Created by XMLmind XSL-FO Converter.
JavatTanitokWebes. Majd a felkínált alapértelmezések elfogadása mellett nyomjunk újabb Next gombokat a
Finish gomb megjelenéséig, melynek nyomásával a projekt létrehozását befejeztük.
Az előző esettanulmányok felélesztéséhez hasonlóan a Source Packages/default package csomagon egy jobb
egérgombot nyomva válasszuk a New/Java Package... menüpontot! A kinyíló ablakban adjuk meg a
javattanitok csomag nevet. Miután a Finish gombbal befejezzük a csomag létrehozását, a projekt
forráskönyvtárában, esetünkben a C:\Documents and Settings\norbi\JavatTanitokWebes\src\java
könyvtárban megjelenik a javattanitok alkönyvtár. Másoljuk ebbe a frissen létrejött könyvtárba a kézikönyv
LabirintusServlet.java forrását. A labirintus API-t most nem másoljuk ide, hanem csak megmondjuk a
NetBeans IDE környezetnek, hogy hol találja a használni kívánt labirintus API-t, azaz a
javattanitok.labirintus csomagot. Például a korábban elkészített javattanitokPeldak.jar Java
archívum állományban. Ehhez a NetBeans IDE projektek fülében a JavatTanitokWebes projekt néven egy
jobb egérgombot nyomva a tulajdonságok Properties menüpontot választva a kinyíló ablak Libraries pontját
kérve, az Add JAR/Folder... gombbal a C:\Documents and
Settings\norbi\JavatTanitokPeldak\dist\JavatTanitokPeldak.jar jar állományt hozzáadva a projekt
fordítása már menni fog.
Page 227
Java esettanulmányok
199 Created by XMLmind XSL-FO Converter.
A projekt futtatásához azonban még a projekt webalkalmazásként való létét is konfigurálnunk kell. Ehhez a
NetBeans IDE projektek fülében a JavatTanitokWebes projekt Configuration Files pontját lenyitva a
web.xml állomány kell kinyitnunk. Itt a Servlet fülre kattintva az alábbi beállításokat végezzük el:
Itt az Add Servlet Element gomb nyomása után a Servlet Class böngészéssel való megadása és a URL
Pattern(s) tetszőleges magadása fontos. A jelen beállítással a javattanitok.LabirintusServlet osztályt a
weben, azaz egy böngészőprogramon keresztül majd a
http://localhost:8084/JavatTanitokWebes/labirintus URL címen érjük el.
Ha ezzel megvagyunk, jöhet a tesztelés, egyszerűen nyomjunk F6 funkcióbillentyűt, vagy a Run/Run Main
Projekt menüpontot! Majd az internetes böngészőnkben keressük fel a
http://localhost:8084/JavatTanitokWebes/labirintus URL címet.
Page 228
Java esettanulmányok
200 Created by XMLmind XSL-FO Converter.
A megfelelő linkek választásával a böngészőben akár győzhetünk is!
Page 229
Java esettanulmányok
201 Created by XMLmind XSL-FO Converter.
1.5.1. A LabirintusServlet osztály
Ha szervletünket a HTTP protokoll GET kérésével felkeressük, akkor a HttpServlet osztály doGet metódusa
fog meghívódni. A hívásban az aktuális paraméterként kapott HttpServletRequest és HttpServletResponse
Page 230
Java esettanulmányok
202 Created by XMLmind XSL-FO Converter.
objektumok reprezentálják a HTTP kérést és választ szervletünk mikrovilágában. A válasz objektumtól elkért
csatorna érdekessége, hogy a másik vége a szervletünket felkereső böngészőben van. Ennek megfelelően a
csatornát karakteresre nyitjuk és szöveget, HTML szöveget nyomunk ki rá. A példa további érdekessége, hogy
arra is példát mutat, hogyan tudunk a szerver oldalon információt megőrizni az egyébként független - de egy
játékostól jövő - HTTP kérések között.
/*
* LabirintusServlet.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
import javattanitok.labirintus.*;
/**
* A labirintus csomag absztrahálta labirintus mikrovilágának egy
* HTTP szervletes életre keltésére ad példát ez az osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.LabirintusVilág
* @see javattanitok.LabirintusApplet
* @see javattanitok.LabirintusJáték
* @see javattanitok.LabirintusMIDlet
* @see javattanitok.HálózatiLabirintus
* @see javattanitok.TávoliLabirintus
* @see javattanitok.KorbásLabirintus
*/
public class LabirintusServlet extends javax.servlet.http.HttpServlet {
/**
* A HTTP GET kérés kiszolgálása.
*
* @param httpKérés a HTTP kérést reprezentáló objektum
* @param httpVálasz a HTTP választ reprezentáló objektum
*/
protected void doGet(javax.servlet.http.HttpServletRequest httpKérés,
javax.servlet.http.HttpServletResponse httpVálasz)
throws javax.servlet.ServletException, java.io.IOException {
// A válasz csatornán küldött adatokat a böngésző
// mint html oldalt értelmezze
httpVálasz.setContentType("text/html;charset=UTF-8");
// Elkérjük a böngészőbe menő csatornát
java.io.PrintWriter csatornaBöngészőbe = httpVálasz.getWriter();
// Elkezdjük beleírni válaszunkat html-ben
csatornaBöngészőbe.println("<html>");
csatornaBöngészőbe.println("<head>");
// A böngészőablek címe
csatornaBöngészőbe.println("<title>Javat tanítok LabirintusServlet</title>");
csatornaBöngészőbe.println("</head>");
csatornaBöngészőbe.println("<body>");
// Ez a böngésző kapcsolatban van már a szerverünkkel?
javax.servlet.http.HttpSession session = httpKérés.getSession();
// A hőst és a labirintust majd ebben a kapcsolatot
// reprezentáló objektumban tároljuk.
Labirintus labirintus = null;
Hős hős = null;
// Ha a kapcsolat most épült fel
if(session.isNew()) {
// akkor elkészítünk egy új labirintust és hőst
csatornaBöngészőbe.println("Helló, új játékot kezdünk!<br>");
labirintus = new Labirintus(6, 3);
hős = new Hős(labirintus);
hős.sor(9);
hős.oszlop(0);
// majd betesszük a kapcsolatot reprezentáló objektumba, hogy a
// legközelebbi kérésével jövő ugyanazon játékos ki tudja venni
session.setAttribute("labirintus", labirintus);
Page 231
Java esettanulmányok
203 Created by XMLmind XSL-FO Converter.
session.setAttribute("hos", hős);
// Különben, azaz, ha már volt kapcslat
} else {
// akkor kivesszük a kapcsolatot reprezentáló objektumból
csatornaBöngészőbe.println("Helló, régi játékot folytatjuk<br>");
labirintus = (Labirintus)session.getAttribute("labirintus");
hős = (Hős)session.getAttribute("hos");
if(hős == null || labirintus == null) {
// Ha esetleg a tesztelés során gond lenne a session kezeléssel...
log("Új labirintust készítettünk...");
labirintus = new Labirintus(6, 3);
hős = new Hős(labirintus);
hős.sor(9);
hős.oszlop(0);
session.setAttribute("labirintus", labirintus);
session.setAttribute("hos", hős);
}
}
// A válasz lapra kiírjuk a hős lépésénél a választási lehetőségeket
// a ?lepes= megfelelő irány formában
csatornaBöngészőbe.println("<br>Merre lépjen a hős?<br>");
csatornaBöngészőbe.println("<a href=\"/JavatTanitokWebes/" +
"labirintus?lepes=fol\">Föl</a>");
csatornaBöngészőbe.println("<br>");
csatornaBöngészőbe.println("<a href=\"/JavatTanitokWebes/" +
"labirintus?lepes=le\">Le</a>");
csatornaBöngészőbe.println("<br>");
csatornaBöngészőbe.println("<a href=\"/JavatTanitokWebes/" +
"labirintus?lepes=jobbra\">Jobbra</a>");
csatornaBöngészőbe.println("<br>");
csatornaBöngészőbe.println("<a href=\"/JavatTanitokWebes/" +
"labirintus?lepes=balra\">Balra</a>");
// A mostani kérésben jött valami infó a lépéssel kapcsolatban?
String lepesString = httpKérés.getParameter("lepes");
// Ha igen, akkor annak megfelelően lépünk
if("fol".equals(lepesString))
hős.lépFöl();
else if("le".equals(lepesString))
hős.lépLe();
else if("jobbra".equals(lepesString))
hős.lépJobbra();
else if("balra".equals(lepesString))
hős.lépBalra();
// Történt ezzel a lépéssel valami érdekes a labirintusban?
switch(labirintus.bolyong(hős)) {
case Labirintus.JÁTÉK_MEGY_HŐS_RENDBEN:
break;
case Labirintus.JÁTÉK_MEGY_MEGHALT_HŐS:
// Még van élete, visszatesszük a kezdő pozícióra
hős.sor(9);
hős.oszlop(0);
break;
case Labirintus.JÁTÉK_VÉGE_MINDEN_KINCS_MEGVAN:
csatornaBöngészőbe.println("<h1>GYŐZTÉL</h1>");
session.invalidate();
break;
case Labirintus.JÁTÉK_VÉGE_MEGHALT_HŐS:
session.invalidate();
csatornaBöngészőbe.println("<h1>VESZTETTÉL</h1>");
break;
}
// Kinyomtatjuk a labirintus aktuális állapotát
csatornaBöngészőbe.println("<pre>");
csatornaBöngészőbe.println(labirintus.kinyomtat(hős));
csatornaBöngészőbe.println("</pre>");
Page 232
Java esettanulmányok
204 Created by XMLmind XSL-FO Converter.
// és néhány infót a játék aktuális adataiból
csatornaBöngészőbe.println("<br>");
csatornaBöngészőbe.println("<br>Életek száma: " + hős.életek());
csatornaBöngészőbe.println("<br>Gyűjtött érték: "+hős.pontszám());
csatornaBöngészőbe.println("</body>");
csatornaBöngészőbe.println("</html>");
// Zárjuk a böngészőbe irányuló csatornát.
csatornaBöngészőbe.close();
}
}
1.6. Java a hálózaton
A következő példák a socket programozás absztrakciós szintjéről indulva egyre magasabb szinteken üzemelnek.
1.6.1. TCP/IP - Hálózati Labirintus
Az Előzetes a példaprogramokból című pontban bevezetett esettanulmány végleges kódját adjuk meg itt.
A példa kipróbálásához szükségünk van a A labirintus API felélesztése című pontban beállított labirintus API-ra,
sőt, ezt ebben a pontban tovább is kell fejlesztenünk. A javattanitok.labirintus csomagbeli Labirintus
osztályt kiterjesztjük a TöbbHősösLabirintus osztállyal, amelyben egy hős halála immár nem jelenti a
labirintus játék végét is egyben.
A HálózatiLabirintus.java és a LabirintusKiszolgálóSzál.java forrásokat másoljuk a
Munkakönyvtár nevű munkakönyvtárunkból nyíló javattanitok könyvtárba, mert ezek az osztályok a
javattanitok csomag részei. A TöbbHősösLabirintus osztályt a javattanitok.labirintus csomag
részeként fejlesztettük ki, ezért a TöbbHősösLabirintus.java állományt a javattanitok/labirintus
könyvtárba másoljuk be.
1.6.1.1. A HálózatiLabirintus osztály
Az osztály elindítja a szerver programot és a játék a LABIRINTUS_PORT kapunál várakozik a kliensekre.
Mindeközben a szokásos párhuzamosan indított programszálon vezérli a labirintus mikrovilágának életét.
Fontos továbbfejlesztés, hogy ugyanabban a - Labirintus osztály továbbfejlesztéseként megírt
TöbbHősösLabirintus osztálybeli - labirintusunkban több hősünk lehet, akiket egy Hashtable
adatszerkezetben foglal csokorba a szerver.
public class HálózatiLabirintus implements Runnable {
/** A játék aktuális labirintusa, minden hálózati hős ebben mozog. */
TöbbHősösLabirintus labirintus;
/** A hősök. */
java.util.Hashtable hősök;
/** Melyik porton megy a játék. */
private static final int LABIRINTUS_PORT = 2006;
A hősök adminisztrációját a HálózatiLabirintus osztály hős() módszere intézi, ami a szerver és a kliensei
közötti kommunikációt lebonyolító LabirintusKiszolgálóSzál a szerver végrehajtási szálával párhuzamosan
futó run() metódusából hívódik.
/*
* HálózatiLabirintus.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
Page 233
Java esettanulmányok
205 Created by XMLmind XSL-FO Converter.
import javattanitok.labirintus.*;
/**
* A labirintus csomag absztrahálta labirintus mikrovilágának egy
* TCP/IP-s hálózati életre keltésére ad példát ez az osztály.
* Tesztelése például a telnet TCP klienssel:
* <pre>
* telnet niobe 2006
* A hős neve?
* Matyi
* Parancsok: l = le, f = föl, j = jobbra, b = balra k = kilép
* sima ENTER = megmutatja a labirintust
*
* --- Labirintusszerinti idő: 13. pillanat -------
* --- Összes hősök száma: 1
* --- Életek száma: 5
* --- Gyűjtött érték: 0
* --- A labirintus: (13. pillanat) -------
* | | | |FAL| |FAL| |FAL|FAL|FAL
* | | | | |K | | | | |
* |FAL| |FAL| |FAL| |FAL| |FAL|
* | | | |K |FAL| |FAL| | |S
* | |FAL|FAL|S | | |FAL|FAL| |FAL
* | | | | |FAL|K | | | |
* | |FAL| | | |FAL|K |FAL|FAL|
* | | |K |FAL| |FAL|S |FAL| |
* | |FAL| | | | | | | |FAL
* |H | | | |FAL| | | |FAL|FAL
* </pre>
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.LabirintusVilág
* @see javattanitok.LabirintusApplet
* @see javattanitok.LabirintusJáték
* @see javattanitok.LabirintusMIDlet
* @see javattanitok.LabirintusServlet
* @see javattanitok.TávoliLabirintus
* @see javattanitok.KorbásLabirintus
* @see javattanitok.ElosztottLabirintus
* @see LabirintusKiszolgálóSzál
*/
public class HálózatiLabirintus implements Runnable {
/** A játék aktuális labirintusa, minden hálózati hős ebben mozog. */
TöbbHősösLabirintus labirintus;
/** A hősök. */
java.util.Hashtable hősök;
/** Melyik porton megy a játék. */
private static final int LABIRINTUS_PORT = 2006;
/** A játékbeli idő mérésére.*/
private long idő = 0;
/** Jelzi a játék végét, ezután a játék álapota már nem változik. */
private boolean játékVége = false;
/**
* Argumentum nélküli konstruktor, gyerekek implicit super()-éhez.
*/
public HálózatiLabirintus(){}
/**
* A <code>HálózatiLabirintus</code> objektum elkészítése.
*
* @param labirintusFájlNév a labirintust definiáló, megfelelő
* szerkezetű szöveges állomány neve.
* @exception RosszLabirintusKivétel ha a labirintust definiáló állomány
* nem a megfelelő szerkezetű
*/
public HálózatiLabirintus(String labirintusFájlNév) throws
RosszLabirintusKivétel {
// A labirintus elkészítése állományból
labirintus = new TöbbHősösLabirintus(labirintusFájlNév);
// A hős elkészítése és a kezdő pozíciójának beállítása
hősök = new java.util.Hashtable();
// A játékbeli idő folyását biztosító szál elkészítése és indítása
Page 234
Java esettanulmányok
206 Created by XMLmind XSL-FO Converter.
new Thread(this).start();
// A TCP szerver indítása
try {
java.net.ServerSocket serverSocket =
new java.net.ServerSocket(LABIRINTUS_PORT);
while(true) {
// Várakozás a játékosok jelentkezésére
java.net.Socket socket = serverSocket.accept();
// akiket külön szálban szolgálunk ki
new LabirintusKiszolgálóSzál(socket, this);
}
} catch(java.io.IOException e) {
e.printStackTrace();
}
}
/**
* A hálózaton keresztül jelentkező hős elkészítése.
*
* @param név a hős neve (= "hoszt IP : név").
* @return Hős a névhez tartozó, esetleg újonan létrehozott hős.
*/
public Hős hős(String név) {
// Ha már létező hős jelentkezett be újra a játékba
if(hősök.containsKey(név))
return (Hős)hősök.get(név);
// Vagy új játékos jön
else {
// aki még nincs a hősök között
// akkor új hősként létrehozzuk
Hős hős = new Hős(labirintus);
// A hős kezdő pozíciója
hős.sor(9);
hős.oszlop(0);
// Felvétele a hősök közé
hősök.put(név, hős);
return hős;
}
}
/**
* A valamikor hálózaton keresztül jelentkező hős törlése.
*
* @param név a hős neve (= "hoszt IP : név").
*/
public void hősMeghalt(String név) {
// Törlés a hősök közül
hősök.remove(név);
}
/**
* A hősök száma.
*
* @return int a hősök száma.
*/
public int hősökSzáma() {
return hősök.size();
}
/**
* A labirintus játék világának ideje.
*
* @return long labirintus játék világának ideje.
*/
public long idő() {
return idő;
}
/**
* A játék aktuális labirintusa, minden hálózati hős ebben mozog.
*
Page 235
Java esettanulmányok
207 Created by XMLmind XSL-FO Converter.
* @return Labirintus a labirintus.
*/
public Labirintus labirintus() {
return labirintus;
}
/** A játék időbeli fejlődésének vezérlése. */
public void run() {
while(!játékVége) {
idoegyseg();
System.out.println("Hősök száma: " + hősök.size());
java.util.Enumeration e = hősök.elements();
while(e.hasMoreElements()) {
Hős hős = (Hős)e.nextElement();
switch(labirintus.bolyong(hős)) {
case TöbbHősösLabirintus.JÁTÉK_MEGY_HŐS_RENDBEN:
break;
case Labirintus.JÁTÉK_MEGY_MEGHALT_HŐS:
hős.sor(9);
hős.oszlop(0);
System.out.println("Megettek a(z) " + idő
+ ". lépésben!");
break;
case Labirintus.JÁTÉK_VÉGE_MINDEN_KINCS_MEGVAN:
System.out.println("Megvan minden kincs a(z) "
+ idő + ". lépésben!");
játékVége = true;
break;
case Labirintus.JÁTÉK_VÉGE_MEGHALT_HŐS:
System.out.println("Minden életem elfogyott a(z) "
+ idő + ". lépésben!");
// Ebben a változatban több hős bolyong,
// így immár egyikük halála nem jelenti a
// játék végét is:
// játékVége = true;
break;
}
}
}
}
/** Megadja, hogy milyen gyorsan telik az idő a játékban. */
private void idoegyseg() {
++idő;
try {
Thread.sleep(1000);
} catch(InterruptedException e) {}
}
/**
* Átveszi a játék indításához szükséges paramétereket, majd
* elindítja a játék világának működését.
*
* @param args a labirintus tervét tartalmazó állomány neve az első
* parancssor-argumentum.
*/
public static void main(String[] args) {
if(args.length != 1) {
System.out.println("Indítás: " +
"java javattanitok.HálózatiLabirintus labirintus.txt");
System.exit(-1);
}
try {
Page 236
Java esettanulmányok
208 Created by XMLmind XSL-FO Converter.
new HálózatiLabirintus(args[0]);
} catch(RosszLabirintusKivétel rosszLabirintusKivétel) {
System.out.println(rosszLabirintusKivétel);
}
}
}
A HálózaiLabirintus szerver accept() függvényében megjelenő kliensekkel a következő osztály
foglalkozik.
1.6.1.2. A LabirintusKiszolgálóSzál osztály
Ez az osztály kommunikál a bejelentkező TCP-s kliensekkel.
/*
* LabirintusKiszolgálóSzál.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
import javattanitok.labirintus.*;
/**
* A labirintus csomag absztrahálta labirintus mikrovilágának
* TCP/IP-s hálózati életre keltését bemutató
* <code>javattanitok.HálózatiLabirintus</code> osztály hálózati
* kiszolgálását végző szál. A kommunikációs socket kapcsolattól
* elkéri a kimeneti és bemeneti csatornát, majd a játékos inputját
* átvéve végtehajtja a hős mozgatását.
*
* Egy pillanatfelvétel a kiszolgálásról:
* <pre>
* telnet niobe 2006
* A hős neve?
* Matyi
* Parancsok: l = le, f = föl, j = jobbra, b = balra k = kilép
* sima ENTER = megmutatja a labirintust
*
* --- Labirintusszerinti idő: 13. pillanat -------
* --- Összes hősök száma: 1
* --- Életek száma: 5
* --- Gyűjtött érték: 0
* --- A labirintus: (13. pillanat) -------
* | | | |FAL| |FAL| |FAL|FAL|FAL
* | | | | |K | | | | |
* |FAL| |FAL| |FAL| |FAL| |FAL|
* | | | |K |FAL| |FAL| | |S
* | |FAL|FAL|S | | |FAL|FAL| |FAL
* | | | | |FAL|K | | | |
* | |FAL| | | |FAL|K |FAL|FAL|
* | | |K |FAL| |FAL|S |FAL| |
* | |FAL| | | | | | | |FAL
* |H | | | |FAL| | | |FAL|FAL
* </pre>
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.HálózatiLabirintus
*/
public class LabirintusKiszolgálóSzál implements Runnable {
/** TCP-s kommunikációs kapcsolat a játékossal. */
Page 237
Java esettanulmányok
209 Created by XMLmind XSL-FO Converter.
java.net.Socket socket;
HálózatiLabirintus hálózatiLabirintus;
/**
* A <code>LabirintusKiszolgálóSzál</code> objektum elkészítése.
*
* @param socket TCP socket kapcsolat a játékossal.
* @param hálózatiLabirintus A labirintust tartalmazó TCP szerver
* <code>javattanitok.HálózatiLabirintus</code> osztály
*/
public LabirintusKiszolgálóSzál(java.net.Socket socket,
HálózatiLabirintus hálózatiLabirintus) {
this.socket = socket;
this.hálózatiLabirintus = hálózatiLabirintus;
new Thread(this).start();
}
/** A jelentkező játékosokat párhuzamosan kiszolgáló szál. */
public void run() {
try {
// A socket kapcsolat feletti bejövő csatorna elkérése
java.io.BufferedReader bejövőCsatorna =
new java.io.BufferedReader(
new java.io.InputStreamReader(socket.
getInputStream()));
// A socket kapcsolat feletti kimenő csatorna elkérése
java.io.PrintWriter kimenőCsatorna =
new java.io.PrintWriter(socket.getOutputStream());
// A hős nevének beolvasása
kimenőCsatorna.println("A hős neve?");
kimenőCsatorna.flush();
String játékostól = bejövőCsatorna.readLine();
// Vagy új vagy régi a hős, a hős neve = "hoszt IP : név"
String hősNév = socket.getInetAddress().getHostAddress() +
" : " + játékostól;
Hős hős = hálózatiLabirintus.hős(hősNév);
// Informáljuk a játékost a játék használatáról
kimenőCsatorna.println("Parancsok: l = le, f = föl, " +
"j = jobbra, b = balra k = kilép");
kimenőCsatorna.println(" sima ENTER = " +
"megmutatja a labirintust");
kimenőCsatorna.flush();
játékostól = bejövőCsatorna.readLine();
while(játékostól != null) {
// A játékostól érkező parancsok feldolgozása
if("l".equals(játékostól))
hős.lépLe();
else if("f".equals(játékostól))
hős.lépFöl();
else if("j".equals(játékostól))
hős.lépJobbra();
else if("b".equals(játékostól))
hős.lépBalra();
else if("k".equals(játékostól))
break;
kimenőCsatorna.println("--- Labirintusszerinti idő: "
+ hálózatiLabirintus.idő() + ". pillanat -------");
kimenőCsatorna.println("--- Összes hősök száma: "
+ hálózatiLabirintus.hősökSzáma());
kimenőCsatorna.println("--- Életek száma: " + hős.életek());
kimenőCsatorna.println("--- Gyűjtött érték: " + hős.pontszám());
kimenőCsatorna.println("--- A labirintus: ("
+ hálózatiLabirintus.idő()+". pillanat) -------");
// Megmutatjuk a labirintus aktuális állapotát
hálózatiLabirintus.labirintus().nyomtat(hős, kimenőCsatorna);
kimenőCsatorna.flush();
if(hős.életek() <= 0) {
hálózatiLabirintus.hősMeghalt(hősNév);
break;
}
játékostól = bejövőCsatorna.readLine();
Page 238
Java esettanulmányok
210 Created by XMLmind XSL-FO Converter.
}
socket.close();
} catch(java.io.IOException e) {
e.printStackTrace();
} finally {
if(socket != null) {
try{
socket.close();
} catch(Exception e) {}
}
}
}
}
1.6.1.3. A TöbbHősösLabirintus osztály
Ebben az osztályban felüldefiniáljuk az ős Labirintus osztály vezérlő bolyong() módszerét, itt egy hős halála
már nincs hatással a játék állapotára.
/*
* TöbbHősösLabirintus.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok.labirintus;
/**
* A több hősös labirintust leíró osztály, ahol egy hős halála
* már nem jelenti a labirintus játék végét. A játék állapotát
* a korábbi játékokban a labirintushoz kapcsoltuk, most, hogy
* olyan továbbfejlesztett labirintust akarunk, amiben több hős
* is bolyonghat, úgy érezzük, hogy a játék vége inkább a hőshöz
* tartozik, semmint a labirintushoz. Mindkettő igaz: mert, ha a
* kincsek fogynak el, akkor a labirintus oldaláról van vége a
* játéknak.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
*/
public class TöbbHősösLabirintus extends Labirintus {
/**
* Argumentum nélküli konstruktor, gyerekek implicit super()-éhez.
*/
public TöbbHősösLabirintus() {}
/**
* A <code>TöbbHősösLabirintus</code> objektum elkészítése.
*
* @param labirintusFájlNév a labirintust definiáló, megfelelő
* szerkezetű szöveges állomány neve.
* @exception RosszLabirintusKivétel ha a labirintust definiáló állomány nem
* a megfelelő szerkezetű
*/
public TöbbHősösLabirintus(String labirintusFájlNév) throws
RosszLabirintusKivétel {
super(labirintusFájlNév);
}
/**
Page 239
Java esettanulmányok
211 Created by XMLmind XSL-FO Converter.
* Az ős megfelelő metódusának elfedése, mert ez a JÁTÉK_VÉGE_MEGHALT_HŐS
* csak a hős végét jelenti, a labirintusét nem!
*
* @see Labirintus#bolyong(Hős hős)
* @param hős aki a labirintusban bolyong.
* @return int a játék állapotát leíró kód.
*/
public int bolyong(Hős hős) {
boolean mindMegvan = true;
for(int i=0; i < kincsek.length; ++i) {
// A hős rátalált valamelyik kincsre?
if(kincsek[i].megtalált(hős))
hős.megtaláltam(kincsek[i]);
// ha ez egyszer is teljesül, akkor nincs minden kincs megtalálva
if(!kincsek[i].megtalálva())
mindMegvan = false;
}
if(mindMegvan) {
játékÁllapot = JÁTÉK_VÉGE_MINDEN_KINCS_MEGVAN;
return játékÁllapot;
}
for(int i=0; i < szörnyek.length; ++i) {
szörnyek[i].lép(hős);
if(szörnyek[i].megesz(hős)) {
if(hős.megettek())
// De ez a játék vége csak a hős végét
// jelenti, a labirintusét nem!
return JÁTÉK_VÉGE_MEGHALT_HŐS;
else
return JÁTÉK_MEGY_MEGHALT_HŐS;
}
}
return JÁTÉK_MEGY_HŐS_RENDBEN;
}
}
1.6.1.4. A HálózatiLabirintus fordítása, futtatása
A szerver oldalon fordítunk és futtatjuk a programot.
[norbi@niobe Munkakönyvtár]$ javac javattanitok/HálózatiLabirintus.java
Note: javattanitok/HálózatiLabirintus.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
[norbi@niobe Munkakönyvtár]$ java javattanitok.HálózatiLabirintus labirintus.txt
Hősök száma: 0
Hősök száma: 0
Hősök száma: 0
...
Page 240
Java esettanulmányok
212 Created by XMLmind XSL-FO Converter.
A szerver futtattása után egy másik ablakban (vagy akár egy másik gépen) futtassuk a klienst, ami most
egyszerűen a telnet program. A telnet parancs után a szerver programot futtató hoszt nevét írjuk, ha ez ugyanaz
a gép, mint amelyiken a klienst is éppen futtatni akarjuk, akkor - akár Windows, akár Linux alatt - a localhost
nevet írhatjuk. A második megadott parancssor-argumentum annak a TCP kapunak a sorszáma, amin a szerver
figyeli a kliensek kapcsolatfelvételi kérelmeit, most a 2006.
[norbi@niobe ~]$ telnet localhost 2006
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
A hős neve?
Matyi
Parancsok: l = le, f = föl, j = jobbra, b = balra k = kilép
sima ENTER = megmutatja a labirintust
--- Labirintusszerinti idő: 36. pillanat -------
--- Összes hősök száma: 1
--- Életek száma: 5
--- Gyűjtött érték: 0
--- A labirintus: (36. pillanat) -------
| | |K |FAL| |FAL| |FAL|FAL|FAL
| | | | | |K |K | | |K
|FAL| |FAL| |FAL| |FAL| |FAL|
| | | | |FAL| |FAL| | |
| |FAL|FAL|SK | | |FAL|FAL| |FAL
| | | | |FAL|K | | |S |
| |FAL| | | |FAL|S |FAL|FAL|
| | | |FAL| |FAL| |FAL| |
| |FAL| | | | | | | |FAL
|H | | | |FAL| | | |FAL|FAL
A szerver oldalon a kliens belépésének megfelelően látjuk, hogy nőtt a hősök száma.
...
Hősök száma: 1
Hősök száma: 1
Hősök száma: 1
...
A játékos a fenti ENTER alkalmazása után most az f felfelé léptető parancsot adja ki:
f
--- Labirintusszerinti idő: 415. pillanat -------
--- Összes hősök száma: 2
--- Életek száma: 5
--- Gyűjtött érték: 0
--- A labirintus: (415. pillanat) -------
| | |K |FAL| |FAL| |FAL|FAL|FAL
| | | | | |K |K | | |K
|FAL| |FAL| |FAL| |FAL| |FAL|
| | | | |FAL| |FAL| | |
| |FAL|FAL|SK | | |FAL|FAL| |FAL
| | | | |FAL|K | | |S |
| |FAL| | | |FAL|S |FAL|FAL|
| | | |FAL| |FAL| |FAL| |
|H |FAL| | | | | | | |FAL
| | | | |FAL| | | |FAL|FAL
Page 241
Java esettanulmányok
213 Created by XMLmind XSL-FO Converter.
Majd a k billentyűvel lépjünk ki a jelen Matyi nevű hősös kliensünkből. Ha újra jelentkezünk egy klienssel
ugyanerről a gépről és a Matyi hősnevet adjuk meg, akkor a szerver emlékezni fog a hős aktuális pozíciójára.
Léptessünk be további hősöket, megint csak egy másik ablakban, vagy akár egy másik gépen! Figyeljük meg,
hogy a szerver tovább növeli a hősök számát. Vegyük észre, hogy a kliensek kilépésétől függetlenül a hősök a
labirintusban maradnak, csak akkor kerülnek ki, ha minden életük elfogy és pusztán akkor nem, ha a játékos
éppen kilép a kliens programból és ennek megfelelően nem jön létre új hős, ha ugyanarról a gépről, ugyanazzal
a hős névvel jelentkezik egy kliens. Mindezt a HálózatiLabirintus osztály hős() módszere intézi, ami a
kommunikációt végző LabirintusKiszolgálóSzál run() metódusából hívódik.
1.6.2. Java RMI - Távoli Labirintus
Az Előzetes a példaprogramokból című pontban bevezetett esettanulmány végleges kódját adjuk meg itt.
A példa kipróbálásához szükségünk van a A labirintus API felélesztése című pontban beállított labirintus API-ra
és az előző pontban továbbfejlesztésként készített TöbbHősösLabirintus osztályra. Sőt, a - szintén az előző
pontbeli - HálózatiLabirintus osztályra is, mert jelen TávoliLabirintus osztályunkat ennek
kiterjesztéseként fogjuk elkészíteni.
Az új TávoliLabirintus.java, TávoliHősíthető.java és a TávoliKliens.java forrásokat másoljuk a
Munkakönyvtár nevű munkakönyvtárunkból nyíló javattanitok könyvtárba, mert ezeket az osztályokat
szokásosan a javattanitok csomag részeiként írjuk meg.
1.6.2.1. A TávoliLabirintus osztály
Az osztály indítja az RMI szervert és TavoliLabirintus néven bejegyzi a távolról elérhető, a
TávoliHősíthatő interfészt implementáló objektumot. Miközben a szokásos párhuzamos programszálon
vezényli a labirintus mikrovilágának életét.
/*
* TávoliLabirintus.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
import javattanitok.labirintus.*;
/**
* A labirintus csomag absztrahálta labirintus mikrovilágának egy
* Java RMI-s hálózati, szerver oldali életre keltésére ad példát
* ez az osztály: a hősök távolról történő jelentkezését és
* mozgatását biztosítja.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.LabirintusVilág
* @see javattanitok.LabirintusApplet
* @see javattanitok.LabirintusJáték
* @see javattanitok.LabirintusMIDlet
* @see javattanitok.LabirintusServlet
* @see javattanitok.HálózatiLabirintus
* @see javattanitok.KorbásLabirintus
* @see javattanitok.ElosztottLabirintus
* @see TávoliKliens
* @see TávoliHősíthető
*/
public class TávoliLabirintus extends HálózatiLabirintus
implements TávoliHősíthető {
/**
* A <code>TávoliLabirintus</code> objektum elkészítése.
*
* @param labirintusFájlNév a labirintust definiáló, megfelelő
* szerkezetű szöveges állomány neve.
Page 242
Java esettanulmányok
214 Created by XMLmind XSL-FO Converter.
* @exception RosszLabirintusKivétel ha a labirintust definiáló állomány
* nem a megfelelő szerkezetű
*/
public TávoliLabirintus(String labirintusFájlNév) throws
RosszLabirintusKivétel {
// A labirintus elkészítése állományból
labirintus = new TöbbHősösLabirintus(labirintusFájlNév);
// A hős elkészítése és a kezdő pozíciójának beállítása
hősök = new java.util.Hashtable();
// A játékbeli idő folyását biztosító szál elkészítése és indítása
new Thread(this).start();
// Az RMI szerver indítása
try {
// A távoli objektum
TávoliHősíthető távoliHősíthető = (TávoliHősíthető)
java.rmi.server.UnicastRemoteObject.exportObject(this, 0);
// bejegyzése a névszolgáltatóba
java.rmi.registry.Registry registry =
java.rmi.registry.LocateRegistry.getRegistry();
registry.bind("TavoliLabirintus", távoliHősíthető);
} catch (java.rmi.AlreadyBoundException be) {
be.printStackTrace();
System.exit(-1);
} catch (java.rmi.RemoteException re) {
re.printStackTrace();
System.out.println("Fut az rmiregistry?");
System.exit(-1);
}
}
/**
* Az RMI-n keresztül jelenetkező hős lépése.
*
* @param hősNév a hős neve.
* @return String a labirintus állapotát bemutató string.
*/
public String lépLe(String hősNév) throws java.rmi.RemoteException {
Hős hős = null;
try {
// a hős neve (= "hoszt IP : név")
hős = hős(java.rmi.server.RemoteServer.getClientHost() +
" : " + hősNév);
} catch(java.rmi.server.ServerNotActiveException e) {
e.printStackTrace();
}
hős.lépLe();
return labirintus.kinyomtat(hős);
}
/**
* Az RMI-n keresztül jelenetkező hős lépése.
*
* @param hősNév a hős neve.
* @return String a labirintus állapotát bemutató string.
*/
public String lépFöl(String hősNév) throws java.rmi.RemoteException {
Hős hős = null;
try {
hős = hős(java.rmi.server.RemoteServer.getClientHost() +
" : " + hősNév);
} catch(java.rmi.server.ServerNotActiveException e) {
e.printStackTrace();
}
hős.lépFöl();
return labirintus.kinyomtat(hős);
Page 243
Java esettanulmányok
215 Created by XMLmind XSL-FO Converter.
}
/**
* Az RMI-n keresztül jelenetkező hős lépése.
*
* @param hősNév a hős neve.
* @return String a labirintus állapotát bemutató string.
*/
public String lépJobbra(String hősNév) throws java.rmi.RemoteException {
Hős hős = null;
try {
hős = hős(java.rmi.server.RemoteServer.getClientHost() +
" : " + hősNév);
} catch(java.rmi.server.ServerNotActiveException e) {
e.printStackTrace();
}
hős.lépJobbra();
return labirintus.kinyomtat(hős);
}
/**
* Az RMI-n keresztül jelenetkező hős lépése.
*
* @param hősNév a hős neve.
* @return String a labirintus állapotát bemutató string.
*/
public String lépBalra(String hősNév) throws java.rmi.RemoteException {
Hős hős = null;
try {
hős = hős(java.rmi.server.RemoteServer.getClientHost() +
" : " + hősNév);
} catch(java.rmi.server.ServerNotActiveException e) {
e.printStackTrace();
}
hős.lépBalra();
return labirintus.kinyomtat(hős);
}
/**
* Átveszi a játék indításához szükséges paramétereket, majd
* elindítja a játék világának működését.
*
* @param args a labirintus tervét tartalmazó állomány neve az első
* parancssor-argumentum.
*/
public static void main(String[] args) {
if(args.length != 1) {
System.out.println("Indítás: " +
"java javattanitok.TávoliLabirintus labirintus.txt");
System.exit(-1);
}
try {
new TávoliLabirintus(args[0]);
} catch(RosszLabirintusKivétel rosszLabirintusKivétel) {
System.out.println(rosszLabirintusKivétel);
}
}
}
Page 244
Java esettanulmányok
216 Created by XMLmind XSL-FO Converter.
1.6.2.2. A TávoliHősíthető osztály
Az interfész a távolról meghívható függvényeket deklarálja.
/*
* TávoliHősíthető.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
/**
* A labirintusba távolról történő jelentkezést biztosít a hősök számára.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see TávoliKliens
* @see TávoliLabirintus
*/
public interface TávoliHősíthető extends java.rmi.Remote {
/**
* Az RMI-n keresztül jelenetkező hős lépése.
*
* @param hősNév a hős neve.
*/
public String lépLe(String hősNév) throws java.rmi.RemoteException;
/**
* Az RMI-n keresztül jelenetkező hős lépése.
*
* @param hősNév a hős neve.
*/
public String lépFöl(String hősNév) throws java.rmi.RemoteException;
/**
* Az RMI-n keresztül jelenetkező hős lépése.
*
* @param hősNév a hős neve.
*/
public String lépJobbra(String hősNév) throws java.rmi.RemoteException;
/**
* Az RMI-n keresztül jelenetkező hős lépése.
*
* @param hősNév a hős neve.
*/
public String lépBalra(String hősNév) throws java.rmi.RemoteException;
}
1.6.2.3. A TávoliKliens osztály
Az osztály a TavoliLabirintus név feloldásával megszerzi a távoli Java RMI objektum referenciáját és (innen
távolról) meghívja például a lépJobbra() módszerét.
/*
* TávoliKliens.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
Page 245
Java esettanulmányok
217 Created by XMLmind XSL-FO Converter.
import javattanitok.labirintus.*;
/**
* A labirintus csomag absztrahálta labirintus mikrovilágának egy
* Java RMI-s hálózati, kliens oldali életre keltésére ad példát
* ez az osztály: a hős távolról történő mozgatását biztosítja.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see TávoliLabirintus
* @see TávoliHősíthető
*/
public class TávoliKliens {
/**
* Elindítja a távolról jelentkező hős klienst.
*
* @param args a hős neve.
*/
public static void main(String[] args) {
String hősNév = null;
// ha nem adtuk meg a parancssor-argumentumot,
// akkor ez az alapértelmezés:
if(args.length != 1)
hősNév = "Matyi";
else
hősNév = args[0];
// Megszerezzük a távoli labirintus (TávoliHősíthető) referenciáját
try {
java.rmi.registry.Registry registry =
java.rmi.registry.LocateRegistry.getRegistry();
TávoliHősíthető távoliHősíthető =
(TávoliHősíthető) registry.lookup("TavoliLabirintus");
// és jobbra mozgatjuk a labirintusban a hősünket
System.out.println(távoliHősíthető.lépJobbra(hősNév));
} catch (java.rmi.NotBoundException be) {
be.printStackTrace();
} catch (java.rmi.RemoteException re) {
re.printStackTrace();
}
}
}
1.6.2.4. A TávoliLabirintus fordítása és futtatása
Egyelőre egy gépen futtatjuk a példát, így lefordítjuk a szervert és a klienst is.
C:\...\Munkakönyvtár> javac javattanitok\TávoliLabirintus.java
C:\...\Munkakönyvtár> javac javattanitok\TávoliKliens.java
Elindítjuk az RMI registry programot, ez a program szolgáltatja a szerveren távolról elérhető objetumok
referenciáit. A távoli objektum referenciájához a bejegyzett - esetünkben a TavoliLabirintus - név szerint
jutunk.
C:\...\Munkakönyvtár> start rmiregistry
Majd futtatjuk a szervert és a klienst.
Page 246
Java esettanulmányok
218 Created by XMLmind XSL-FO Converter.
C:\...\Munkakönyvtár> java javattanitok.TávoliLabirintus labirintus.txt
Hősök száma: 0
Hősök száma: 0
Hősök száma: 0
...
C:\...\Munkakönyvtár> java javattanitok.TávoliKliens
| | | |FAL| |FAL| |FAL|FAL|FAL
| | | | | | |S | | |
|FAL| |FAL| |FAL| |FAL|K |FAL|
| | | | |FAL|K |FAL| | |
| |FAL|FAL| | |K |FAL|FAL| |FAL
| | | | |FAL| | | |S |K
| |FAL| |S |K |FAL| |FAL|FAL|
| | | |FAL| |FAL| |FAL| |
| |FAL| | | | | | | |FAL
| |H | | |FAL| | | |FAL|FAL
C:\...\Munkakönyvtár> java javattanitok.TávoliKliens
| | | |FAL| |FAL| |FAL|FAL|FAL
| | | | | |S | | | |
|FAL| |FAL| |FAL| |FAL|K |FAL|
| | | | |FAL|K |FAL| | |
| |FAL|FAL| | |K |FAL|FAL| |FAL
| | | | |FAL| | | | |K
| |FAL| |S |K |FAL|S |FAL|FAL|
| | | |FAL| |FAL| |FAL| |
| |FAL| | | | | | | |FAL
| | |H | |FAL| | | |FAL|FAL
Másoljuk át egy Linuxos gépre a példát és a TávoliKliens.java forrást módosítsuk, hogy ne a localhost-hoz
akarjon kapcsolódni, hanem a távoli, a kalapacs nevű, természetesen a szervert futtató gépen keresse az
rmiregistry programot:
java.rmi.registry.Registry registry =
java.rmi.registry.LocateRegistry.getRegistry("kalapacs");
[norbi@niobe Munkakönyvtár]$ javac javattanitok/TávoliKliens.java
[norbi@niobe Munkakönyvtár]$ java javattanitok.TávoliKliens
| | | |FAL| |FAL| |FAL|FAL|FAL
| | | | | | | | | |
|FAL| |FAL| |FAL| |FAL|K |FAL|
| | | | |FAL|K |FAL| | |
| |FAL|FAL| | |SK |FAL|FAL| |FAL
| | | | |FAL| | | | |K
| |FAL| |S |K |FAL| |FAL|FAL|
| | | |FAL| |FAL|S |FAL| |
| |FAL| | | | | | | |FAL
| |H | | |FAL| | | |FAL|FAL
[norbi@niobe Munkakönyvtár]$ java javattanitok.TávoliKliens
| | | |FAL| |FAL| |FAL|FAL|FAL
| | | | | | | | | |
|FAL| |FAL| |FAL| |FAL|K |FAL|
| | | | |FAL|K |FAL| | |
Page 247
Java esettanulmányok
219 Created by XMLmind XSL-FO Converter.
| |FAL|FAL| | |K |FAL|FAL| |FAL
| | | | |FAL|S | | | |K
| |FAL| |S |K |FAL| |FAL|FAL|
| | | |FAL| |FAL|S |FAL| |
| |FAL| | | | | | | |FAL
| | |H | |FAL| | | |FAL|FAL
1.6.3. CORBA - Korbás Labirintus
Az Előzetes a példaprogramokból című pontban bevezetett esettanulmány végleges kódját adjuk meg itt.
1.6.3.1. A KorbásLabirintus osztály
Az osztály elindítja a CORBA szervert: elkészíti a kiszolgáló CORBA objektumot, amit TavoliHos néven a
CORBA névszolgáltatóba is bejegyez. Miközben a szokásosan indított párhuzamos programszálban vezérli a
labirintus mikrovilágának életét.
A szerver CORBA specifikus logikáját külön is részletezzük. A CORBA világában a programok az ORB
szoftveren, az Object Request Broker, a metódushívások közvetítőjén keresztül tudnak kapcsolódni. A szerver
első dolga ezért - az esetünkben a localhost 2006-os kapujánál futó - ORB szerverrel felvenni a kapcsolatot.
// Az ORB tulajdonságainak megadása
java.util.Properties orbTulajdonságok = new java.util.Properties();
orbTulajdonságok.put("org.omg.CORBA.ORBInitialHost", "localhost");
orbTulajdonságok.put("org.omg.CORBA.ORBInitialPort", "2006");
// Az ORB (a metódushívások közvetítőjének) inicializálása
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init((String [])null,
orbTulajdonságok);
A CORBA környezetben méginkább igaz, ami Javaban is igaz volt: minden objektum. S ez itt szinte minden
programsorban következetesen tetten érhető. Mert például a szerver következő lépése egy objektum adapter
elérése. Az objektum adapterek éltetik a kiszolgáló CORBA objektumainkat, s itt jön a következetesség: ezek az
adapterek maguk is CORBA objektumok. Az adapterek fába szervezhetők, attól függően, hogy milyen
tulajdonságokkal akarjuk felruházni őket, most a gyökér POA-t használjuk.
// A gyökér POA CORBA objektum referenciájának megszerzése
org.omg.CORBA.Object corbaObjektum =
orb.resolve_initial_references("RootPOA");
Az ORB-től elkért objektumok általános org.omg.CORBA.Object osztálybeli objektumok, amiket a CORBA
világában honos típuskényszerítéssel a megfelelő típusúra kasztolunk a céltípus Helper osztályának narrow
módszerét használva:
// CORBA-s típuskényszerítéssel a megfelelőre
org.omg.PortableServer.POA gyökérPoa =
org.omg.PortableServer.POAHelper.narrow(corbaObjektum);
Majd a közben elkészített kiszolgáló CORBA objektumunkat TavoliHos néven bejegyezzük a
névszolgáltatóba.
Page 248
Java esettanulmányok
220 Created by XMLmind XSL-FO Converter.
A CORBA OO világ telefonkönyve a CORBA névszolgáltató. Ez a szolgáltatás is CORBA objektumként került
megvalósításra. A névszolgáltatás egy fa szerkezetbe van szervezve, ahol a gyökeret reprezentáló CORBA
objektum referenciáját ugyanúgy szerezzük meg, mint az imént a POA adapterét.
// A névszolgáltató gyökerének, mint CORBA objektum
// referenciájának megszerzése
corbaObjektum =
orb.resolve_initial_references("NameService");
org.omg.CosNaming.NamingContextExt névszolgáltatóGyökere
= org.omg.CosNaming.NamingContextExtHelper
.narrow(corbaObjektum);
A CORBA névszolgáltatása
Mint már említettük, CORBA-ban is minden objektum, maga a névszolgáltatás is. Ennek megfelelően
a névszolgáltatást a megfelelő IDL nyelvű interfész megismerésén keresztül is tanulmányozhatja az
érdeklődő Olvasó. Ezt a cosnaming.idl állományban találjuk meg a
http://www.omg.org/docs/formal/04-10-07.txt címen.
/*
* KorbásLabirintus.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
import javattanitok.labirintus.*;
/**
* A labirintus csomag absztrahálta labirintus mikrovilágának egy
* CORBA-s, szerver oldali életre keltésére ad példát ez az osztály:
* a hősök távolról történő jelentkezését és mozgatását biztosítja.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.LabirintusVilág
* @see javattanitok.LabirintusApplet
* @see javattanitok.LabirintusJáték
* @see javattanitok.LabirintusMIDlet
* @see javattanitok.LabirintusServlet
* @see javattanitok.HálózatiLabirintus
* @see javattanitok.TávoliLabirintus
* @see javattanitok.ElosztottLabirintus
* @see TávoliHősKiszolgáló
* @see KorbásKliens
* @see tavolihos.idl
*/
public class KorbásLabirintus extends HálózatiLabirintus {
/**
* A <code>TávoliLabirintus</code> objektum elkészítése.
*
* @param labirintusFájlNév a labirintust definiáló, megfelelő
* szerkezetű szöveges állomány neve.
* @exception RosszLabirintusKivétel ha a labirintust definiáló állomány nem
* a megfelelő szerkezetű
*/
public KorbásLabirintus(String labirintusFájlNév) throws
RosszLabirintusKivétel {
// A labirintus elkészítése állományból
labirintus = new TöbbHősösLabirintus(labirintusFájlNév);
// A hősöket tartalmazó adatszerkezet elkészítése
hősök = new java.util.Hashtable();
// A játék valóságának (világának) indítása:
Page 249
Java esettanulmányok
221 Created by XMLmind XSL-FO Converter.
new Thread(this).start();
// A CORBA szerver indítása
try{
// Az ORB tulajdonságainak megadása
java.util.Properties orbTulajdonságok = new java.util.Properties();
orbTulajdonságok.put("org.omg.CORBA.ORBInitialHost", "localhost");
orbTulajdonságok.put("org.omg.CORBA.ORBInitialPort", "2006");
// Az ORB (a metódushívások közvetítőjének) inicializálása
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init((String [])null,
orbTulajdonságok);
// A gyökér POA CORBA objektum referenciájának megszerzése
org.omg.CORBA.Object corbaObjektum =
orb.resolve_initial_references("RootPOA");
// CORBA-s típuskényszerítéssel a megfelelőre
org.omg.PortableServer.POA gyökérPoa =
org.omg.PortableServer.POAHelper.narrow(corbaObjektum);
// A POA kiszolgáló állapotba kapcsolása:
gyökérPoa.the_POAManager().activate();
// A kiszolgáló objektum létrehozása
TávoliHősKiszolgáló távoliHősKiszolgáló =
new TávoliHősKiszolgáló(this);
// CORBA objektum referencia elkészítése
corbaObjektum =
gyökérPoa.servant_to_reference(távoliHősKiszolgáló);
// CORBA-s típuskényszerítéssel a megfelelőre
javattanitok.korbas.TavoliHos távoliHős =
javattanitok.korbas.TavoliHosHelper.narrow(corbaObjektum);
// A névszolgáltató gyökerének, mint CORBA objektum
// referenciájának megszerzése
corbaObjektum =
orb.resolve_initial_references("NameService");
org.omg.CosNaming.NamingContextExt névszolgáltatóGyökere
= org.omg.CosNaming.NamingContextExtHelper
.narrow(corbaObjektum);
// A kiszolgáló objektum bejegyzése a névszolgáltatóba
org.omg.CosNaming.NameComponent név[] =
névszolgáltatóGyökere.to_name("TavoliHos");
névszolgáltatóGyökere.rebind(név, távoliHős);
// Várakozás a játékosok jelentkezésére
orb.run();
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
}
/**
* Átveszi a játék indításához szükséges paramétereket, majd
* elindítja a játék világának működését.
*
* @param args a labirintus tervét tartalmazó állomány neve az első
* parancssor-argumentum.
*/
public static void main(String[] args) {
if(args.length != 1) {
System.out.println("Indítás: " +
"java javattanitok.KorbásLabirintus labirintus.txt");
System.exit(-1);
}
try {
new KorbásLabirintus(args[0]);
} catch(RosszLabirintusKivétel rosszLabirintusKivétel) {
System.out.println(rosszLabirintusKivétel);
}
}
Page 250
Java esettanulmányok
222 Created by XMLmind XSL-FO Converter.
}
1.6.3.2. A KorbásKliens osztály
A kliens a szerverhez hasonlóan felveszi a kapcsolatot az ORB szoftverrel, majd azon keresztül megszerzi a
névszolgáltató objektum referenciáját, akitől elkéri a TavoliHos névhez bejegyzett kiszolgáló CORBA
objetumunk referenciáját, akitől a hős jobbra léptetését kéri.
/*
* KorbásKliens.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
import javattanitok.labirintus.*;
/**
* A labirintus csomag absztrahálta labirintus mikrovilágának egy
* CORBA-s hálózati, kliens oldali életre keltésére ad példát ez az osztály:
* a hős távolról történő mozgatását biztosítja.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see KorbásLabirintus
* @see TávoliHősKiszolgáló
*/
public class KorbásKliens {
/**
* Elindítja a távolról jelentkező hős klienst.
*
* @param args a hős neve.
*/
public static void main(String[] args) {
String hősNév = null;
// ha nem adtuk meg a parancssor-argumentumot,
// akkor ez az alapértelmezés:
if(args.length != 1)
hősNév = "Matyi";
else
hősNév = args[0];
try {
// Az ORB tulajdonságainak megadása
java.util.Properties orbTulajdonságok = new java.util.Properties();
orbTulajdonságok.put("org.omg.CORBA.ORBInitialHost", "localhost");
orbTulajdonságok.put("org.omg.CORBA.ORBInitialPort", "2006");
// Az ORB (a metódushívások közvetítőjének) inicializálása
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init((String [])null,
orbTulajdonságok);
// A névszolgáltató gyökerének, mint CORBA objektum
// referenciájának megszerzése
org.omg.CORBA.Object corbaObjektum =
orb.resolve_initial_references("NameService");
org.omg.CosNaming.NamingContextExt névszolgáltatóGyökere
= org.omg.CosNaming.
NamingContextExtHelper.narrow(corbaObjektum);
// A TavoliHos szolgáltatást nyújtó CORBA objektum
// referenciájának megszerzése
javattanitok.korbas.TavoliHos távoliHős =
javattanitok.korbas.TavoliHosHelper.narrow(
névszolgáltatóGyökere.resolve_str("TavoliHos"));
System.out.println(távoliHős.lépJobbra(hősNév));
Page 251
Java esettanulmányok
223 Created by XMLmind XSL-FO Converter.
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.6.3.3. A TavoliHos interfész
A kiszolgáló CORBA objektum távolról is elérhető módszereit egy IDL nyelvű interfészben deklaráljuk, az
alábbi tavolihos.idl állományban.
module javattanitok
{
module korbas
{
interface TavoliHos
{
string lépLe(in string hosNev);
string lépFöl(in string hosNev);
string lépJobbra(in string hosNev);
string lépBalra(in string hosNev);
};
};
};
Az egymásba ágyazott két IDL modul a javattanitok.korbas Java csomagra képeződik majd le. Az idlj
fordító majd az ennek megfelelő javattanitok/korbas könyvtárba generálja le a CORBA specifikus Java
forrásokat.
1.6.3.4. A TávoliHősKiszolgáló osztály
Ebben az osztályban kell megadnunk az IDL interfészben deklarált módszerek implementációját. A kiszolgáló
objektum megvalósítására több lehetőség adódik, most azt választjuk, hogy az osztályt az interfésznek
megfelelő POA osztályból, esetünkben a TavoliHosPOA osztályból származtatjuk.
/*
* TávoliHősKiszolgáló.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
import javattanitok.labirintus.*;
/**
* A <code>tavolihos.idl</code> leírta TávoliHős CORBA
* kiszolgáló objektum megvalósítása.
*
* A <code>tavolihos.idl</code> interfész:
* <pre>
* module javattanitok
* {
* module korbas
* {
* interface TavoliHos
* {
* string lépLe(in string hosNev);
* string lépFöl(in string hosNev);
* string lépJobbra(in string hosNev);
* string lépBalra(in string hosNev);
Page 252
Java esettanulmányok
224 Created by XMLmind XSL-FO Converter.
* };
* };
* };
* </pre>
* A <code>javattanitok.korbas.*</code> csomag legenerálása:
* <pre>
* idlj -fall tavolihos.idl
* </pre>
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see KorbásLabirintus
* @see KorbásKliens
*/
public class TávoliHősKiszolgáló
extends javattanitok.korbas.TavoliHosPOA {
/** A labirintus játékot futtató osztály. */
KorbásLabirintus korbásLabirintus;
/**
* A <code>TávoliHősKiszolgáló</code> objektum elkészítése.
*
* @param korbásLabirintus A labirintus játékot futtató osztály.
*/
public TávoliHősKiszolgáló(KorbásLabirintus korbásLabirintus) {
this.korbásLabirintus = korbásLabirintus;
}
/**
* Az ORB-n keresztül jelenetkező hős lépése.
*
* @param hősNév a hős neve.
* @return String a labirintus állapotát bemutató string.
*/
public String lépLe(String hősNév) {
Hős hős = null;
hős = korbásLabirintus.hős(hősNév);
hős.lépLe();
return korbásLabirintus.labirintus().kinyomtat(hős);
}
/**
* Az ORB-n keresztül jelenetkező hős lépése.
*
* @param hősNév a hős neve.
* @return String a labirintus állapotát bemutató string.
*/
public String lépFöl(String hősNév) {
Hős hős = null;
hős = korbásLabirintus.hős(hősNév);
hős.lépFöl();
return korbásLabirintus.labirintus().kinyomtat(hős);
}
/**
* Az ORB-n keresztül jelenetkező hős lépése.
*
* @param hősNév a hős neve.
* @return String a labirintus állapotát bemutató string.
*/
public String lépJobbra(String hősNév) {
Hős hős = null;
hős = korbásLabirintus.hős(hősNév);
hős.lépJobbra();
return korbásLabirintus.labirintus().kinyomtat(hős);
}
Page 253
Java esettanulmányok
225 Created by XMLmind XSL-FO Converter.
/**
* Az ORB-n keresztül jelenetkező hős lépése.
*
* @param hősNév a hős neve.
* @return String a labirintus állapotát bemutató string.
*/
public String lépBalra(String hősNév) {
Hős hős = null;
hős = korbásLabirintus.hős(hősNév);
hős.lépBalra();
return korbásLabirintus.labirintus().kinyomtat(hős);
}
}
1.6.3.5. A KorbásLabirintus fordítása és futtatása
Az idlj fordító használatát a Objektumok mindenütt: a CORBA OO világ című pontban vezettük be.
C:\...\Munkakönyvtár> idlj -fall tavolihos.idl
A tavolihos.idl modul szerkezetének megfelelően a kliens és a szerveroldali CORBA specifikus Java
források megjelentek a javattanitok/korbas könyvtárban. Lefordítjuk a szervert
C:\...\Munkakönyvtár> javac javattanitok\KorbásLabirintus.java
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
és a kliens oldalt
C:\...\Munkakönyvtár> javac javattanitok\KorbásKliens.java
Indítjuk az ORB szoftvert:
C:\Documents and Settings\norbi> start orbd -ORBInitialPort 2006
Majd a szerveroldalt:
C:\...\Munkakönyvtár> java javattanitok.KorbásLabirintus labirintus.txt
Hősök száma: 0
Hősök száma: 0
Hősök száma: 0
...
és a kliens oldalt:
Page 254
Java esettanulmányok
226 Created by XMLmind XSL-FO Converter.
C:\...\Munkakönyvtár> java javattanitok.KorbásKliens
| | |K |FAL| |FAL| |FAL|FAL|FAL
| | | |S | | | |S | |
|FAL| |FAL| |FAL|K |FAL| |FAL|K
| | | | |FAL| |FAL| | |
| |FAL|FAL| | | |FAL|FAL| |FAL
| | | | |FAL| | | | |
| |FAL|K | | |FAL| |FAL|FAL|
| | |SK |FAL| |FAL| |FAL| |K
| |FAL| | | | | | | |FAL
| |H | | |FAL| | | |FAL|FAL
C:\...\Munkakönyvtár> java javattanitok.KorbásKliens
| | |K |FAL| |FAL| |FAL|FAL|FAL
| | | | | | | | | |
|FAL| |FAL| |FAL|K |FAL| |FAL|K
| | | | |FAL| |FAL|S | |
| |FAL|FAL| | | |FAL|FAL| |FAL
| | | | |FAL| | | | |
| |FAL|K |S | |FAL| |FAL|FAL|
| | |K |FAL| |FAL| |FAL| |K
| |FAL|S | | | | | | |FAL
| | |H | |FAL| | | |FAL|FAL
Izgalmasabb a tesztelés, ha a klienst átmásoljuk egy másik gépre, s az alábbi módosítás
orbTulajdonságok.put("org.omg.CORBA.ORBInitialHost", "kalapacs");
majd fordítás után futtatjuk ezen a másik gépen, a niobén:
[norbi@niobe Munkakönyvtár]$ java javattanitok.KorbásKliens Herkules
| | | |FAL| |FAL| |FAL|FAL|FAL
| | |K | |K | | | | |
|FAL| |FAL| |FAL| |FAL| |FAL|
| | | | |FAL|K |FAL| | |
| |FAL|FAL| | | |FAL|FAL|S |FAL
| | | |K |FAL|K | | | |
| |FAL| | | |FAL| |FAL|FAL|S
| | |K |FAL| |FAL| |FAL|S |
| |FAL| | | | | | | |FAL
| |H | | |FAL| | | |FAL|FAL
[norbi@niobe Munkakönyvtár]$ java javattanitok.KorbásKliens Herkules
| | | |FAL| |FAL| |FAL|FAL|FAL
| | |K | |K | | | | |
|FAL| |FAL| |FAL| |FAL| |FAL|
| | | | |FAL|K |FAL| | |
| |FAL|FAL| | | |FAL|FAL|S |FAL
| | | |K |FAL|K | | | |
| |FAL| | | |FAL| |FAL|FAL|S
| | |K |FAL| |FAL| |FAL|S |
| |FAL| | | | | | | |FAL
| | |H | |FAL| | | |FAL|FAL
1.7. Elosztott objektumok - Elosztott labirintus
Az Előzetes a példaprogramokból című pontban bevezetett esettanulmány végleges kódját adjuk meg itt.
Page 255
Java esettanulmányok
227 Created by XMLmind XSL-FO Converter.
1.7.1. Az elosztott labirintus API felélesztése
Feltesszük, hogy a korábbi esettanulmányok alapján a labirintus API-t már felélesztettük, sőt a hálózati
példáknál kifejlesztett TöbbHősösLabirintus osztállyal is bővítettük már ezt az API-t. Az alábbiakban az
elosztott labirintus API-t állítjuk be.
Tetszőleges munkakönyvtárunkban hozzuk létre - a labirintus API rendelkezésre állása miatt már létező -
javattanitok könyvtáron belül az elosztott könyvtárat.
C:\...\Munkakönyvtár> mkdir javattanitok\elosztott
Ezzel a javattanitok.elosztott Java csomagot leképeztük a munkakönyvtárunkba, a kifejlesztendő
osztályokat ide, a javattanitok/elosztott könyvtárban írjuk majd meg, azaz gyűjtük most össze. Másoljuk
ide a következő SzereplőKiszolgáló.java, HősKiszolgáló.java, KincsKiszolgáló.java,
SzörnyKiszolgáló.java, LabirintusKiszolgáló.java állományokat! A következő pontokban ismertetjük
ezeknek - az elosztott.idl interfészben deklarált CORBA objektumoknak - az implementációit.
1.7.1.1. A SzereplőKiszolgáló osztály
A kiszolgáló objektumokban implementálnunk kell az elosztott.idl interfészben specifikált metódusokat,
illetve lekérdező és beállító módszereket kell írnunk a szereplő attribútumokhoz. A Szereplo IDL interfésznek
nincsenek módszerei, így csupán az oszlop, sor attribútumaihoz kell egy-egy, a tulajdonságokkal megegyező
nevű beállító és lekérdező függvényt implementálnunk az interfészt kiszolgáló CORBA objektum, a
SzereplőKiszolgáló osztály testében.
/*
* SzereplőKiszolgáló.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok.elosztott;
/**
* Az <code>elosztott.idl</code> leírta Szereplő CORBA objektumot
* kiszolgáló objektum megvalósítása.
*
* Az <code>elosztott.idl</code> interfész:
* <pre>
* module javattanitok
* {
* module elosztott
* {
* module objektumok
* {
* interface Szereplo
* {
* attribute long oszlop;
* attribute long sor;
* };
* interface Kincs;
* interface Hos : Szereplo
* {
* attribute long megtalaltErtekek;
* readonly attribute long eletek;
* void megtalaltam(in Kincs kincs);
* boolean megettek();
* };
* interface Kincs : Szereplo
* {
* attribute long ertek;
* readonly attribute boolean megtalalva;
Page 256
Java esettanulmányok
228 Created by XMLmind XSL-FO Converter.
* boolean megtalalt(in Hos hos);
* };
* interface Szorny : Szereplo
* {
* boolean lep(in Hos hos);
* };
* interface Labirintus
* {
* wstring belepHos(in Hos hos);
* wstring belepKincs(in Kincs kincs);
* wstring belepSzorny(in Szorny szorny);
* };
* };
* };
* };
* </pre>
* A <code>javattanitok.elosztott.objektumok.*</code> csomag legenerálása:
* <pre>
* idlj -fall -falltie elosztott.idl
* </pre>
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see ElosztottLabirintus
* @see ElosztottKliens
* @see LabirintusKiszolgáló
* @see HősKiszolgáló
* @see SzörnyKiszolgáló
* @see KincsKiszolgáló
*/
public class SzereplőKiszolgáló
extends javattanitok.elosztott.objektumok.SzereploPOA {
/** A szereplő oszlop pozíciója. */
int oszlop;
/** A szereplő sor pozíciója. */
int sor;
/** A <code>SzereplőKiszolgáló</code> objektum elkészítése. */
public SzereplőKiszolgáló() {}
/**
* Beállítja a szereplő labirintusbeli pozíciójának oszlop
* koordinátáját.
*
* @param oszlop a szereplő labirintusbeli pozíciójának oszlop
* koordinátája.
*/
public void oszlop(int oszlop) {
this.oszlop = oszlop;
System.out.println("SZEREPLŐ> Beállították a pozíciómat.");
}
/**
* Megadja a szereplő labirintusbeli pozíciójának oszlop koordinátáját.
*
* @return int a szereplő labirintusbeli pozíciójának oszlop koordinátája.
*/
public int oszlop() {
System.out.println("SZEREPLŐ> Lekérdezték a pozíciómat.");
return oszlop;
}
/**
* Beállítja a szereplő labirintusbeli pozíciójának sor koordinátáját.
*
* @param sor a szereplő labirintusbeli pozíciójának sor koordinátája.
*/
public void sor(int sor) {
this.sor = sor;
}
Page 257
Java esettanulmányok
229 Created by XMLmind XSL-FO Converter.
/**
* Megadja a szereplő labirintusbeli pozíciójának sor koordinátáját.
*
* @return int a szereplő labirintusbeli pozíciójának sor koordinátája.
*/
public int sor() {
return sor;
}
}
1.7.1.2. A HősKiszolgáló osztály
Az osztályt a SzereplőKiszolgáló osztályból szeretnénk származtatni, s mivel Javaban nincs többszörös
öröklődés, így nem tudjuk a CORBA objektum implementációjánál a szokásos utunkat követni: a HosPOA idlj
generálta osztályt kiterjeszteni. Ezért a Hos IDL interfészt kiszolgáló CORBA objektum elkészítésénél a tie
delegációs modellt követjük: kiterjesztjük a SzereplőKiszolgáló-t és implementáljuk a HosOperations idlj
generálta, az IDL interfészben deklarált metódusok specifikációját tartalmazó Java interfészt.
/*
* HősKiszolgáló.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok.elosztott;
// Itt vannak a CORBA objektumaink, bár nem szokásunk,
// most importálunk, mert a teljes csomagnévvel
// való osztálynév minősítést, a hossza miatt nehézkes
// lenne kezelni a könyv kapcsán a szövegszerkesztőnkben.
import javattanitok.elosztott.objektumok.*;
/**
* Az <code>elosztott.idl</code> leírta Hős CORBA objektumot
* kiszolgáló objektum megvalósítása.
*
* Az <code>elosztott.idl</code> interfész:
* <pre>
* module javattanitok
* {
* module elosztott
* {
* module objektumok
* {
* interface Szereplo
* {
* attribute long oszlop;
* attribute long sor;
* };
* interface Kincs;
* interface Hos : Szereplo
* {
* attribute long megtalaltErtekek;
* readonly attribute long eletek;
* void megtalaltam(in Kincs kincs);
* boolean megettek();
* };
* interface Kincs : Szereplo
* {
* attribute long ertek;
* readonly attribute boolean megtalalva;
* boolean megtalalt(in Hos hos);
* };
* interface Szorny : Szereplo
* {
Page 258
Java esettanulmányok
230 Created by XMLmind XSL-FO Converter.
* boolean lep(in Hos hos);
* };
* interface Labirintus
* {
* wstring belepHos(in Hos hos);
* wstring belepKincs(in Kincs kincs);
* wstring belepSzorny(in Szorny szorny);
* };
* };
* };
* };
* </pre>
* A <code>javattanitok.elosztott.objektumok.*</code> csomag legenerálása:
* <pre>
* idlj -fall -falltie elosztott.idl
* </pre>
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see ElosztottLabirintus
* @see ElosztottKliens
* @see LabirintusKiszolgáló
* @see SzereplőKiszolgáló
* @see HősKiszolgáló
* @see SzörnyKiszolgáló
* @see KincsKiszolgáló
*/
public class HősKiszolgáló
extends SzereplőKiszolgáló
implements HosOperations {
/** A labirintusban megtalált kincsek értékei. */
protected int megtaláltÉrtékek;
/** A hős életeinek maximális száma. */
public static final int ÉLETEK_SZÁMA = 5;
/** A hős életeinek száma. */
protected int életekSzáma = ÉLETEK_SZÁMA;
/** A <code>HősKiszolgáló</code> objektum elkészítése. */
public HősKiszolgáló() {}
/**
* Életek számának lekérdezése.
*
* @return int az életek száma.
*/
public int eletek() {
return életekSzáma;
}
/**
* A megtalált értékek beállítása.
*
* @param megtaláltÉrtékek a megtalált értékek.
*/
public void megtalaltErtekek(int megtaláltÉrtékek) {
this.megtaláltÉrtékek = megtaláltÉrtékek;
}
/**
* A megtalált értékek lekérdezése.
*
* @return int a megtalált értékek.
*/
public int megtalaltErtekek() {
return megtaláltÉrtékek;
}
/**
* Jelzi, hogy éppen megettek.
*
* @return true ha a hősnek még van élete, ellenkező esetben,
* azaz ha az összes élete elfogyott már, akkor false.
*/
Page 259
Java esettanulmányok
231 Created by XMLmind XSL-FO Converter.
public boolean megettek() {
if(életekSzáma > 0) {
--életekSzáma;
return false;
} else
return true;
}
/**
* Gyüjtögeti a megtalált kincseket.
*
* @param kincs amit éppen magtalált a hős.
*/
public void megtalaltam(Kincs kincs) {
megtaláltÉrtékek += kincs.ertek();
System.out.println("HŐS> Kincset találtam.");
}
}
1.7.1.3. A KincsKiszolgáló osztály
Az osztály megvalósításánál a HősKiszolgáló osztálynál is alkalmazott módszert követjük.
/*
* KincsKiszolgáló.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok.elosztott;
/**
* Az <code>elosztott.idl</code> leírta Kincs CORBA objektumot
* kiszolgáló objektum megvalósítása.
*
* Az <code>elosztott.idl</code> interfész:
* <pre>
* module javattanitok
* {
* module elosztott
* {
* module objektumok
* {
* interface Szereplo
* {
* attribute long oszlop;
* attribute long sor;
* };
* interface Kincs;
* interface Hos : Szereplo
* {
* attribute long megtalaltErtekek;
* readonly attribute long eletek;
* void megtalaltam(in Kincs kincs);
* boolean megettek();
* };
* interface Kincs : Szereplo
* {
* attribute long ertek;
* readonly attribute boolean megtalalva;
* boolean megtalalt(in Hos hos);
* };
* interface Szorny : Szereplo
* {
Page 260
Java esettanulmányok
232 Created by XMLmind XSL-FO Converter.
* boolean lep(in Hos hos);
* };
* interface Labirintus
* {
* wstring belepHos(in Hos hos);
* wstring belepKincs(in Kincs kincs);
* wstring belepSzorny(in Szorny szorny);
* };
* };
* };
* };
* </pre>
* A <code>javattanitok.elosztott.objektumok.*</code> csomag legenerálása:
* <pre>
* idlj -fall -falltie elosztott.idl
* </pre>
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see ElosztottLabirintus
* @see ElosztottKliens
* @see LabirintusKiszolgáló
* @see SzereplőKiszolgáló
* @see HősKiszolgáló
* @see SzörnyKiszolgáló
*/
public class KincsKiszolgáló
extends SzereplőKiszolgáló
implements javattanitok.elosztott.objektumok.KincsOperations {
/** A kincs értéke. */
protected int érték;
/** Megtaláltak már? */
protected boolean megtalálva;
/** A <code>KincsKiszolgáló</code> objektum elkészítése. */
public KincsKiszolgáló() {}
/**
* Az érték beállítása.
*
* @param érték az a kincs értéke.
*/
public void ertek(int érték) {
this.érték = érték;
}
/**
* Az érték lekérdezése.
*
* @return int az érték.
*/
public int ertek() {
return érték;
}
/**
* Megtalált ez a hős?
*
* @param hős aki rámbukkant éppen.
* @return boolean megtalál?
*/
public boolean megtalalt(javattanitok.elosztott.objektumok.Hos hős) {
if(megtalálva) {
System.out.println("KINCS> Már korábban rámtalált egy hős.");
// mert egy kicset csak egyszer lehet megtalálni
return false;
}
if(oszlop == hős.oszlop() && sor == hős.sor()) {
megtalálva = true;
System.out.println("KINCS> Rámtalált.");
Page 261
Java esettanulmányok
233 Created by XMLmind XSL-FO Converter.
} else
System.out.println("KINCS> Nem talált rám.");
return megtalálva;
}
/**
* Megmondja, hogy megtalálták-e már a kincset?
*
* @return true ha a kincset már megtalálták,
* ha még nem akkor false.
*/
public boolean megtalalva() {
return megtalálva;
}
}
1.7.1.4. A SzörnyKiszolgáló osztály
Az osztály megvalósításánál a HősKiszolgáló osztálynál is alkalmazott módszert követjük.
/*
* SzörnyKiszolgáló.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok.elosztott;
/**
* Az <code>elosztott.idl</code> leírta Szörny CORBA objektumot
* kiszolgáló objektum megvalósítása.
*
* Az <code>elosztott.idl</code> interfész:
* <pre>
* module javattanitok
* {
* module elosztott
* {
* module objektumok
* {
* interface Szereplo
* {
* attribute long oszlop;
* attribute long sor;
* };
* interface Kincs;
* interface Hos : Szereplo
* {
* attribute long megtalaltErtekek;
* readonly attribute long eletek;
* void megtalaltam(in Kincs kincs);
* boolean megettek();
* };
* interface Kincs : Szereplo
* {
* attribute long ertek;
* readonly attribute boolean megtalalva;
* boolean megtalalt(in Hos hos);
* };
* interface Szorny : Szereplo
* {
* boolean lep(in Hos hos);
* };
* interface Labirintus
* {
Page 262
Java esettanulmányok
234 Created by XMLmind XSL-FO Converter.
* wstring belepHos(in Hos hos);
* wstring belepKincs(in Kincs kincs);
* wstring belepSzorny(in Szorny szorny);
* };
* };
* };
* };
* </pre>
* A <code>javattanitok.elosztott.objektumok.*</code> csomag legenerálása:
* <pre>
* idlj -fall -falltie elosztott.idl
* </pre>
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see ElosztottLabirintus
* @see ElosztottKliens
* @see LabirintusKiszolgáló
* @see SzereplőKiszolgáló
* @see HősKiszolgáló
* @see SzereplőKiszolgáló
* @see KincsKiszolgáló
*/
public class SzörnyKiszolgáló
extends SzereplőKiszolgáló
implements javattanitok.elosztott.objektumok.SzornyOperations {
/** A <code>SzörnyKiszolgáló</code> objektum elkészítése. */
public SzörnyKiszolgáló() {}
/**
* Lépek a hős felé.
*
* @param hős aki felé mozgok.
* @return boolean elkaptam ez a hőst?
*/
public boolean lep(javattanitok.elosztott.objektumok.Hos hos) {
// ... a hős felé lépést nem implementáltuk ebben a példában.
System.out.println("SZÖRNY> Lépek egy hős felé.");
// ... soha nem eszem meg ebben az implementációban.
return false;
}
}
1.7.1.5. A LabirintusKiszolgáló osztály
/*
* LabirintusKiszolgáló.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok.elosztott;
import java.util.List;
import java.util.Iterator;
// Itt vannak a CORBA objektumaink, bár nem szokásunk,
// most importálunk, mert a teljes csomagnévvel
// való osztálynév minősítést, a hossza miatt nehézkes
// lenne kezelni a könyv kapcsán a szövegszerkesztőnkben.
import javattanitok.elosztott.objektumok.*;
/**
* Az <code>elosztott.idl</code> leírta Labirintus CORBA objektumot
* kiszolgáló objektum megvalósítása.
*
* Az <code>elosztott.idl</code> interfész:
* <pre>
* module javattanitok
Page 263
Java esettanulmányok
235 Created by XMLmind XSL-FO Converter.
* {
* module elosztott
* {
* module objektumok
* {
* interface Szereplo
* {
* attribute long oszlop;
* attribute long sor;
* };
* interface Kincs;
* interface Hos : Szereplo
* {
* attribute long megtalaltErtekek;
* readonly attribute long eletek;
* void megtalaltam(in Kincs kincs);
* boolean megettek();
* };
* interface Kincs : Szereplo
* {
* attribute long ertek;
* readonly attribute boolean megtalalva;
* boolean megtalalt(in Hos hos);
* };
* interface Szorny : Szereplo
* {
* boolean lep(in Hos hos);
* };
* interface Labirintus
* {
* wstring belepHos(in Hos hos);
* wstring belepKincs(in Kincs kincs);
* wstring belepSzorny(in Szorny szorny);
* };
* };
* };
* };
* </pre>
* A <code>javattanitok.elosztott.objektumok.*</code> csomag legenerálása:
* <pre>
* idlj -fall -falltie elosztott.idl
* </pre>
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see ElosztottLabirintus
* @see ElosztottKliens
* @see SzereplőKiszolgáló
* @see HősKiszolgáló
* @see SzörnyKiszolgáló
* @see KincsKiszolgáló
*/
public class LabirintusKiszolgáló
extends javattanitok.labirintus.TöbbHősösLabirintus
implements LabirintusOperations, Runnable {
/** A labirintus szörnyei, elemei távoli CORBA objektumok. */
protected List<Szorny> szörnyek;
/** A labirintus kincsei, elemei távoli CORBA objektumok. */
protected List<Kincs> kincsek;
/** A labirintus hősei, elemei távoli CORBA objektumok. */
protected List<Hos> hősök;
/** A játékbeli idő mérésére.*/
private long idő = 0;
/** Véletlenszám generátor a szereplők mozgatásához. */
protected static java.util.Random véletlenGenerátor =
new java.util.Random();
/** A <code>LabirintusKiszolgáló</code> objektum elkészítése. */
public LabirintusKiszolgáló() {
// Az ős megfelelő konstruktorának hívása
super();
// A kincseket tartalmazó adatszerkezet létrehozása,
// elemei majd távoli CORBA objektumok lesznek
Page 264
Java esettanulmányok
236 Created by XMLmind XSL-FO Converter.
kincsek = new java.util.ArrayList<Kincs>();
// A kincseket tartalmazó adatszerkezet létrehozása,
// elemei majd távoli CORBA objektumok lesznek
szörnyek = new java.util.ArrayList<Szorny>();
// A kincseket tartalmazó adatszerkezet létrehozása,
// elemei majd távoli CORBA objektumok lesznek
hősök = java.util.Collections.synchronizedList(
new java.util.ArrayList<Hos>());
// A játékbeli idő folyását biztosító szál elkészítése és indítása
new Thread(this).start();
}
/**
* A Hos CORBA objektum az ORB-n keresztül belép a labirintusba.
*
* @param hős a Hos CORBA objektum referenciája.
* @return String a labirintus állapotát bemutató string.
*/
public String belepHos(Hos hős) {
System.out.println("LABIRINTUS> Belépett egy hős: " + hős);
hősök.add(hős);
szereplőHelyeKezdetben(hős);
return toString();
}
/**
* A Kincs CORBA objektum az ORB-n keresztül belép a labirintusba.
*
* @param hős a Kincs CORBA objektum referenciája.
* @return String a labirintus állapotát bemutató string.
*/
public String belepKincs(Kincs kincs) {
System.out.println("LABIRINTUS> Belépett egy kincs.");
kincsek.add(kincs);
szereplőHelyeKezdetben(kincs);
return toString();
}
/**
* A Szorny CORBA objektum az ORB-n keresztül belép a labirintusba.
*
* @param hős a Szorny CORBA objektum referenciája.
* @return String a labirintus állapotát bemutató string.
*/
public String belepSzorny(Szorny szörny) {
System.out.println("LABIRINTUS> Belépett egy szörny.");
szörnyek.add(szörny);
szereplőHelyeKezdetben(szörny);
return toString();
}
/**
* A szereplő labirintusbeli kezdő pozíciójának meghatározása.
*/
void szereplőHelyeKezdetben(Szereplo szereplő) {
// Többször próbálkozunk elhelyezni a szereplőt a labirintusban,
// számolja, hol tartunk ezekkel a próbálkozásokkal:
int számláló = 0, oszlop = 0, sor = 0;
do {
// itt +2,-2-k, hogy a bal alsó saroktól távol tartsuk
// a szereplőket, mert majd ezt akarjuk a hős kezdő pozíciójának
oszlop = 2+véletlenGenerátor.nextInt(szélesség-2);
sor = véletlenGenerátor.nextInt(magasság-2);
// max. 10-szer próbálkozunk, de ha sikerül nem "falba tenni" a
// szereplőt, akkor máris kilépünk:
} while(++számláló<10 && fal(oszlop, sor));
Page 265
Java esettanulmányok
237 Created by XMLmind XSL-FO Converter.
szereplő.oszlop(oszlop);
szereplő.sor(sor);
}
/** A játék időbeli fejlődésének vezérlése. */
public void run() {
while(true) {
idoegyseg();
System.out.println(this);
synchronized(hősök) {
Iterator i = hősök.iterator();
while(i.hasNext()) {
Hos hős = (Hos)i.next();
try {
switch(bolyong(hős)) {
case JÁTÉK_MEGY_HŐS_RENDBEN:
break;
case JÁTÉK_MEGY_MEGHALT_HŐS:
break;
case JÁTÉK_VÉGE_MEGHALT_HŐS:
i.remove();
break;
}
} catch (org.omg.CORBA.SystemException e) {
i.remove();
System.out.println("LABIRINTUS> " +
"Egy hőst eltávolítottam.");
}
}
}
}
}
/**
* Az ős megfelelő metódusának elfedése, az új CORBA szereplőket
* használva.
*
* @see TöbbHősösLabirintus#bolyong(Hős hős)
* @param hős aki a labirintusban bolyong.
* @return int a játék állapotát leíró kód.
*/
public int bolyong(Hos hős) {
for(Kincs kincs : kincsek) {
if(kincs.megtalalt(hős))
hős.megtalaltam(kincs);
}
for(Szorny szörny : szörnyek) {
if(szörny.lep(hős)) {
if(hős.megettek())
return JÁTÉK_VÉGE_MEGHALT_HŐS;
else
return JÁTÉK_MEGY_MEGHALT_HŐS;
}
}
return JÁTÉK_MEGY_HŐS_RENDBEN;
Page 266
Java esettanulmányok
238 Created by XMLmind XSL-FO Converter.
}
/**
* Megadja, hogy milyen gyorsan telik az idő a játékban.
*/
private void idoegyseg() {
++idő;
try {
Thread.sleep(1000);
} catch(InterruptedException e) {}
}
/**
* Madadja, hogy van-e szörny a labirintus adott oszlop,
* sor pozíciója.
*
* @param oszlop a labirintus adott oszlopa
* @param sor a labirintus adott sora
* @return true ha van.
*/
boolean vanSzörny(int sor, int oszlop) {
boolean van = false;
for(Szorny szörny: szörnyek)
if(sor == szörny.sor()
&& oszlop == szörny.oszlop()) {
van = true;
break;
}
return van;
}
/**
* Madadja, hogy van-e megtalálható kincs a labirintus
* adott oszlop, sor pozíciója.
*
* @param oszlop a labirintus adott oszlopa
* @param sor a labirintus adott sora
* @return true ha van.
*/
boolean vanKincs(int sor, int oszlop) {
boolean van = false;
for(Kincs kincs: kincsek)
if(sor == kincs.sor()
&& oszlop == kincs.oszlop()
&& !kincs.megtalalva()) {
van = true;
break;
}
return van;
}
/**
* Kinyomtatja a labirintus szerkezetét és szereplőit a System.out-ra.
*
* @param hős akit szintén belenyomtat a labirintusba.
*/
public void nyomtat(Hos hős) {
for(int i=0; i<magasság; ++i) {
for(int j=0; j<szélesség; ++j) {
boolean vanSzörny = vanSzörny(i, j);
boolean vanKincs = vanKincs(i, j);
boolean vanHős = (i == hős.sor() && j == hős.oszlop());
if(szerkezet[i][j])
System.out.print("|FAL");
else if(vanSzörny && vanKincs && vanHős)
Page 267
Java esettanulmányok
239 Created by XMLmind XSL-FO Converter.
System.out.print("|SKH");
else if(vanSzörny && vanKincs)
System.out.print("|SK ");
else if(vanKincs && vanHős)
System.out.print("|KH ");
else if(vanSzörny && vanHős)
System.out.print("|SH ");
else if(vanKincs)
System.out.print("|K ");
else if(vanHős)
System.out.print("|H ");
else if(vanSzörny)
System.out.print("|S ");
else
System.out.print("| ");
}
System.out.println();
}
}
/**
* A labirintus sztring reprezentációja.
*
* @return String labirintus sztring reprezentációja.
*/
public String toString() {
return " Idő:" + idő
+ " hős:" + hősök.size()
+ " kincs:" + kincsek.size()
+ " szörny:" + szörnyek.size();
}
}
1.7.1.6. A CORBA interfész definíciók
Az előző pontokban ismertetett öt Java osztály a elosztott.idl interfész definíciós állományban leírt öt
CORBA objektum kiszolgálói. A valóságban persze ezzel kezdődik a tervezés. A játék OO világát ezekkel az
IDL nyelvű interfészekkel feszítjük ki. Ezt követheti az implementációs nyelv kiválasztása, ami esetünkben a
Java, majd az ennek a választásnak megfelelő fordítót, esetünkben a JDK-beli idlj fordítót használva a CORBA
kommunikációhoz szükséges Java források legenerálása és a saját kiszolgáló objektumok megírása. Ez királyi
út, csupán az implementálandó szolgáltatások megvalósítására kell figyelnünk.
module javattanitok
{
module elosztott
{
module objektumok
{
interface Szereplo
{
attribute long oszlop;
attribute long sor;
};
interface Kincs;
interface Hos : Szereplo
{
attribute long megtalaltErtekek;
readonly attribute long eletek;
void megtalaltam(in Kincs kincs);
boolean megettek();
};
interface Kincs : Szereplo
{
attribute long ertek;
readonly attribute boolean megtalalva;
Page 268
Java esettanulmányok
240 Created by XMLmind XSL-FO Converter.
boolean megtalalt(in Hos hos);
};
interface Szorny : Szereplo
{
boolean lep(in Hos hos);
};
interface Labirintus
{
wstring belepHos(in Hos hos);
wstring belepKincs(in Kincs kincs);
wstring belepSzorny(in Szorny szorny);
};
};
};
};
1.7.1.7. Az ElosztottLabirintus osztály
Ez az osztály tölti be leginkább a szerver szerepét, mert ő élteti a labirintust kiszolgáló objektumot.
/*
* ElosztottLabirintus.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
import javattanitok.elosztott.*;
/**
* Az elosztott CORBA-s labirintus csomag absztrahálta elosztott labirintus
* mikrovilágának egy szerver oldali (delegációs POA/Tie leképezéses) életre
* keltésére ad példát ez az osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.LabirintusVilág
* @see javattanitok.LabirintusApplet
* @see javattanitok.LabirintusJáték
* @see javattanitok.LabirintusMIDlet
* @see javattanitok.LabirintusServlet
* @see javattanitok.HálózatiLabirintus
* @see javattanitok.TávoliLabirintus
* @see ElosztottKliens
* @see LabirintusKiszolgáló
* @see elosztott.idl
*/
public class ElosztottLabirintus {
/** A <code>ElosztottLabirintus</code> objektum elkészítése. */
public ElosztottLabirintus() {
// A CORBA szerver indítása
try{
// Az ORB tulajdonságainak megadása
java.util.Properties orbTulajdonságok =
new java.util.Properties();
orbTulajdonságok.put("org.omg.CORBA.ORBInitialPort", "2006");
orbTulajdonságok.put("org.omg.CORBA.ORBInitialHost", "localhost");
// Az ORB (a metódushívások közvetítőjének) inicializálása
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init((String [])null,
orbTulajdonságok);
// A gyökér POA CORBA objektum referenciájának megszerzése
org.omg.CORBA.Object corbaObjektum = orb.
resolve_initial_references("RootPOA");
// CORBA-s típuskényszerítéssel a megfelelőre
org.omg.PortableServer.POA gyökérPoa =
org.omg.PortableServer.POAHelper.narrow(corbaObjektum);
Page 269
Java esettanulmányok
241 Created by XMLmind XSL-FO Converter.
// A POA kiszolgáló állapotba kapcsolása:
gyökérPoa.the_POAManager().activate();
// A Labirintus kiszolgáló objektum elkészítése,
// a delegációs/POA/Tie modell szerinti leképezéssel
javattanitok.elosztott.objektumok.LabirintusPOATie tie =
new javattanitok.elosztott.objektumok.LabirintusPOATie(
new LabirintusKiszolgáló(), gyökérPoa);
// A Labirintus CORBA objektum
javattanitok.elosztott.objektumok.Labirintus labirintus =
tie._this(orb);
// A névszolgáltató gyökerének, mint CORBA objektum
// referenciájának megszerzése
corbaObjektum =
orb.resolve_initial_references("NameService");
org.omg.CosNaming.NamingContextExt névszolgáltatóGyökere
= org.omg.CosNaming.
NamingContextExtHelper.narrow(corbaObjektum);
// A kiszolgáló objektum bejegyzése a névszolgáltatóba
org.omg.CosNaming.NameComponent név[] =
névszolgáltatóGyökere.to_name("ElosztottLabirintus");
névszolgáltatóGyökere.rebind(név, labirintus);
// Várakozás a szereplők (hősök, kincsek, szörnyek) jelentkezésére
orb.run();
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
}
/**
* Elindítja az elosztott labirintust világának labirintus részét.
*
* @param args nincsenek parancssor-argumentumok.
*/
public static void main(String[] args) {
new ElosztottLabirintus();
}
}
1.7.1.8. Az ElosztottKliens osztály
Ez az osztály tölti be leginkább a kliens szerepét, mert ő élteti a labirintust szereplőit kiszolgáló objektumokat,
akiket számtalan esetben hívnak vissza más szereplők vagy a labirintus: ebben az értelemben viszont szerver.
/*
* ElosztottKliens.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
import javattanitok.elosztott.*;
/**
* Az elosztott CORBA-s labirintus csomag absztrahálta elosztott labirintus
* mikrovilágának egy kliens oldali életre keltésére ad példát ez az osztály,
* mely egyben a szereplő objektumok visszahívható szervere is.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see ElosztottLabirintus
* @see SzereplőKiszolgáló
Page 270
Java esettanulmányok
242 Created by XMLmind XSL-FO Converter.
* @see HősKiszolgáló
* @see SzörnyKiszolgáló
* @see KincsKiszolgáló
*/
public class ElosztottKliens implements Runnable {
org.omg.CORBA.ORB orb; // hogy a run()-ból elérjük őket
javattanitok.elosztott.objektumok.Labirintus labirintus;
/**
* Elkészíti a távol gépre bejelentkező hős, szörny vagy
* kincs visszahívható kliens CORBA objektumot. Hősként
* való indításakor feldolgozza a játékostól jövő billentyű
* parancsokat.
*
* @param args első tagja "hős" - Hos objektum, "kincs" -
* Kincs objektum, "szörny" - Szorny objektum lesz a kliens,
* alapértelmezés a "kincs".
*/
public ElosztottKliens(String[] args) {
String ki = null;
// ha nem adtuk meg a parancssor-argumentumot,
// akkor ez az alapértelmezés:
if(args.length != 1)
ki = "Kincs";
else
ki = args[0];
try {
// Az ORB tulajdonságainak megadása
java.util.Properties orbTulajdonságok = new java.util.Properties();
orbTulajdonságok.put("org.omg.CORBA.ORBInitialHost",
"172.21.0.152");
orbTulajdonságok.put("org.omg.CORBA.ORBInitialPort", "2006");
// Az ORB (a metódushívások közvetítőjének) inicializálása
orb = org.omg.CORBA.ORB.init((String [])null,
orbTulajdonságok);
// A névszolgáltató gyökerének, mint CORBA objektum
// referenciájának megszerzése
org.omg.CORBA.Object corbaObjektum =
orb.resolve_initial_references("NameService");
org.omg.CosNaming.NamingContextExt névszolgáltatóGyökere
= org.omg.CosNaming.
NamingContextExtHelper.narrow(corbaObjektum);
// A labirintus CORBA objektum referenciájának megszerzése
labirintus =
javattanitok.elosztott.objektumok.
LabirintusHelper.narrow(névszolgáltatóGyökere.
resolve_str("ElosztottLabirintus"));
// A gyökér POA CORBA objektum referenciájának megszerzése
corbaObjektum = orb.resolve_initial_references("RootPOA");
// CORBA-s típuskényszerítéssel a megfelelőre
org.omg.PortableServer.POA gyökérPoa =
org.omg.PortableServer.POAHelper.narrow(corbaObjektum);
// A POA kiszolgáló állapotba kapcsolása:
gyökérPoa.the_POAManager().activate();
// Miként indították ezt a klienst?
if("hős".equals(ki)) {
// Ha a kliens hősként működik
// A Hos kiszolgáló objektum elkészítése,
// a delegációs/POA/Tie modell szerinti leképezéssel
javattanitok.elosztott.objektumok.HosPOATie tie =
new javattanitok.elosztott.objektumok.HosPOATie(
new HősKiszolgáló(), gyökérPoa);
// A Hos CORBA objektum
javattanitok.elosztott.objektumok.Hos hős = tie._this(orb);
System.out.println("HŐS> ELkészültem: " + hős);
// Távoli metódus hívása, a távoli CORBA labirintus
// objektumnak átadkuk a Hos CORBA objektum referenciáját
System.out.println(labirintus.belepHos(hős));
Page 271
Java esettanulmányok
243 Created by XMLmind XSL-FO Converter.
new Thread(this).start();
// CORBA szerverként is üzemelünk, mert a Hos
// objektumot a Labirintus objektum majd visszahívogatja
orb.run();
} else if("szörny".equals(ki)) {
// Ha a kliens szörnyként működik
javattanitok.elosztott.objektumok.SzornyPOATie tie =
new javattanitok.elosztott.objektumok.SzornyPOATie(
new SzörnyKiszolgáló(), gyökérPoa);
javattanitok.elosztott.objektumok.Szorny szörny =
tie._this(orb);
System.out.println(labirintus.belepSzorny(szörny));
orb.run();
} else {
// Ha a kliens kincsként működik
javattanitok.elosztott.objektumok.KincsPOATie tie =
new javattanitok.elosztott.objektumok.KincsPOATie(
new KincsKiszolgáló(), gyökérPoa);
javattanitok.elosztott.objektumok.Kincs kincs =
tie._this(orb);
System.out.println(labirintus.belepKincs(kincs));
orb.run();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/** A hős játékostól jövő billentyűzet input feldolgozása. */
public void run() {
try {
// Feldolgozzuk a játékostól jövő billentyű parancsokat
java.io.BufferedReader konzol =
new java.io.BufferedReader(
new java.io.InputStreamReader(System.in));
String parancs = null;
while((parancs = konzol.readLine()) != null) {
System.out.println(parancs);
// A Hos CORBA objektum kilép a labirintusból
if("k".equals(parancs))
break;
// További lépés parancsok itt implemetálva: föl, le stb. ...
}
} catch(java.io.IOException e) {
System.out.println(e);
}
orb.shutdown(false);
System.exit(0);
}
/** Az elosztott labirintus klienseinek indítása.
*
* @param args első tagja "hős" - Hos objektum, "kincs" -
* Kincs objektum, "szörny" - Szorny objektum lesz a kliens,
* alapértelmezés a "kincs".
*/
public static void main(String[] args) {
new ElosztottKliens(args);
Page 272
Java esettanulmányok
244 Created by XMLmind XSL-FO Converter.
}
}
1.7.2. Az Elosztott labirintus fordítása és futtatása
A 172.21.0.152 gépen egy parancsablakban futtatjuk az idlj fordítót, hogy megkapjuk az alkalmazásunkhoz
tartozó CORBA specifikus Java forrásokat. Ezek az elosztott.idl interfész definíciós állomány modul
szerkezetének megfelelően a javattanitok\elosztott\objektumok könyvtárba íródnak ki. Ezután az egész
projektünket lefordítjuk.
C:\...\Munkakönyvtár> idlj -fall -falltie elosztott.idl
C:\...\Munkakönyvtár> javac javattanitok\*.java javattanitok\labirintus\*.java
javattanitok\elosztott\*.java
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
A 172.21.0.152 gépen, mondjuk egy másik parancsablakban elindítjuk az orbd ORB démont. A démonnal
együtt a névszolgáltatás is elindul. Az ElosztottLabirintus szerver objektum a kiszolgáló
LabirintusKiszolgáló CORBA objektumot majd ElosztottLabirintus néven jegyzi be ide. Annak a
CORBA kliens alkalmazásnak, aki el akarja érni ezt a bejegyzett LabirintusKiszolgáló CORBA objektumot,
nem kell mást tennie, mint megszerezni a 172.21.0.152 hoszt 2006-os kapujánál figyelő ORB névszolgáltatást
végző objektumának referenciáját és ettől az objektumtól elkérni a nála bejegyzett ElosztottLabirintus
névhez tartozó CORBA objektum eferenciát, de ne szaladjunk ennyire messzire, egyelőre indítsuk tehát az ORB
démont!
C:\Documents and Settings\norbi> start orbd -ORBInitialPort 2006 -ORBInitialHost
172.21.0.152
Az elosztott labirintus egy gépen
Ha a kedves Olvasó egyetlen gépen szeretné tesztelni a példát, akkor a parancssorban és a
ElosztottKliens.java forrásban a 172.21.0.152 helyett használja a localhost-ot!
Ugyancsak a 172.21.0.152 gépen, mondjuk abban a parancsablakban, melyben a fordítást végeztük, indítsuk
el a labirintus CORBA objektumot, pontosabban az objektumot elkészítő és a névszolgáltatóban bejegyző
szervert, az ElosztottLabirintus osztályt!
C:\...\Munkakönyvtár> java javattanitok.ElosztottLabirintus
Idő:1 hős:0 kincs:0 szörny:0
Idő:2 hős:0 kincs:0 szörny:0
Idő:3 hős:0 kincs:0 szörny:0
...
A labirintusunk időfejlődésének minden pillanatában kiírja fő jellemzőit, hogy éppen hány hős, kincs és szörny
van benne.
Még mindig ugyanazon a 172.21.0.152 gépen, de egy másik parancsablakban futtassunk egy kincset!
C:\...\Munkakönyvtár> java javattanitok.ElosztottKliens
Page 273
Java esettanulmányok
245 Created by XMLmind XSL-FO Converter.
SZEREPLŐ> Beállították a pozíciómat.
Idő:12 hős:0 kincs:1 szörny:0
A parancssor paraméter nélkül induló ElosztottKliens CORBA kliens KincsKiszolgáló objektumot készít,
a névszolgáltatón keresztül megszerzi a labirintus referenciáját, majd átadja a kliensként - a most éppen
kincsként - elkészített CORBA objektum referenciáját a labirintusnak. Aki immár majd ezt a referenciát
felhasználva anélkül tudja visszahívni a kincs klienst, hogy a kincs bármely névszolgáltatóba be lenne jegyezve.
Közben a Parancssor - java javattanitok.ElosztottLabirintus ablakban a labirintus logolja a
bejelentkező klienst.
...
Idő:10 hős:0 kincs:0 szörny:0
Idő:11 hős:0 kincs:0 szörny:0
LABIRINTUS> Belépett egy kincs.
Idő:12 hős:0 kincs:1 szörny:0
Idő:13 hős:0 kincs:1 szörny:0
...
Továbbra is ugyanazon a 172.21.0.152 gépen, de megint egy újabb parancsablakban futtassunk most egy
szörnyet!
C:\...\Munkakönyvtár> java javattanitok.ElosztottKliens szörny
SZEREPLŐ> Beállították a pozíciómat.
Idő:42 hős:0 kincs:1 szörny:1
...
Közben megint csak a Parancssor - java javattanitok.ElosztottLabirintus ablakban a labirintus
logolja az éppen bejelentkett szörny klienst.
...
Idő:40 hős:0 kincs:1 szörny:0
Idő:41 hős:0 kincs:1 szörny:0
LABIRINTUS> Belépett egy szörny.
Idő:42 hős:0 kincs:1 szörny:1
Idő:43 hős:0 kincs:1 szörny:1
...
Még továbbra is ugyanazon a 172.21.0.152 gépen, de megint csak egy újabb parancsablakban futtassunk most
egy hőst!
C:\...\Munkakönyvtár> java javattanitok.ElosztottKliens hős
HŐS> ELkészültem: IOR:000000000000002e49444c3a6a6176617474616e69746f6b2f656c6f73
7a746f74742f6f626a656b74756d6f6b2f486f733a312e3000000000000001000000000000008600
0102000000000e3137322e31372e3133352e3631000d7800000031afabcb0000000020ffe655a100
000001000000000000000100000008526f6f74504f41000000000800000001000000001400000000
00000200000001000000200000000000010001000000020501000100010020000101090000000100
01010000000026000000020002
SZEREPLŐ> Beállították a pozíciómat.
Idő:68 hős:1 kincs:1 szörny:1
SZEREPLŐ> Lekérdezték a pozíciómat.
SZEREPLŐ> Lekérdezték a pozíciómat.
Page 274
Java esettanulmányok
246 Created by XMLmind XSL-FO Converter.
Interoperable Object Reference
Csupán az érdekesség kedvéért kiírattuk a (kliens) hős objektum referenciáját (címét).
Közben a Parancssor - java javattanitok.ElosztottLabirintus ablakban a labirintus logolja a
bejelentkező hőst.
...
Idő:66 hős:0 kincs:1 szörny:1
Idő:67 hős:0 kincs:1 szörny:1
LABIRINTUS> Belépett egy hős: IOR:000000000000002e49444c3a6a6176617474616e69746
f6b2f656c6f737a746f74742f6f626a656b74756d6f6b2f486f733a312e300000000000000100000
00000000086000102000000000e3137322e31372e3133352e3631000d7800000031afabcb0000000
020ffe655a100000001000000000000000100000008526f6f74504f4100000000080000000100000
00014000000000000020000000100000020000000000001000100000002050100010001002000010
109000000010001010000000026000000020002
Idő:68 hős:1 kincs:1 szörny:1
Idő:69 hős:1 kincs:1 szörny:1
...
Interoperable Object Reference
Nem meglepő módon ezt az IOR referenciát ismeri a szerver oldal is.
A hős belépését követően - programozásunknak megfelelően - felélednek az eddig szunnyadó kliens ablakok. A
Parancssor - java javattanitok.ElosztottKliens ablakban a kincs logolását látjuk.
...
KINCS> Nem talált rám.
KINCS> Nem talált rám.
KINCS> Nem talált rám.
...
A Parancssor - java javattanitok.ElosztottKliens szörny ablakban a szörny objektum logolását
látjuk.
...
SZÖRNY> Lépek egy hős felé.
SZÖRNY> Lépek egy hős felé.
SZÖRNY> Lépek egy hős felé.
...
Most beléptetünk néhány további szereplőt egy másik, a 172.21.0.17 gépről. Hasonlóan itt is futtatjuk az idlj
fordítót és lefordítjuk a projektet.
C:\...\Munkakönyvtár> idlj -fall -falltie elosztott.idl
C:\...\Munkakönyvtár> javac javattanitok\*.java java ttanitok\labirintus\*.java
javattanitok\elosztott\*.java
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Page 275
Java esettanulmányok
247 Created by XMLmind XSL-FO Converter.
Majd most ebből az ablakból is indítunk egy kincset.
C:\...\Munkakönyvtár> java javattanitok.ElosztottKliens
SZEREPLŐ> Beállították a pozíciómat.
KINCS> Nem talált rám.
Idő:293 hős:1 kincs:2 szörny:1
KINCS> Nem talált rám.
KINCS> Nem talált rám.
KINCS> Nem talált rám.
Közben a másik, a 172.21.0.152 gép Parancssor - java javattanitok.ElosztottLabirintus ablakban
a labirintus logolja a most bejelentkező kincs klienst.
...
Idő:291 hős:1 kincs:1 szörny:1
Idő:292 hős:1 kincs:1 szörny:1
LABIRINTUS> Belépett egy kincs.
Idő:293 hős:1 kincs:2 szörny:1
Idő:294 hős:1 kincs:2 szörny:1
...
Továbbra is a 172.21.0.17 gépen, de megint egy újabb parancsablakban futtassunk most egy szörnyet!
C:\...\Munkakönyvtár> java javattanitok.ElosztottKliens szörny
SZEREPLŐ> Beállították a pozíciómat.
SZÖRNY> Lépek egy hős felé.
Idő:327 hős:1 kincs:2 szörny:2
SZÖRNY> Lépek egy hős felé.
SZÖRNY> Lépek egy hős felé.
...
Közben megint csak a 172.21.0.152 gép, Parancssor - java javattanitok.ElosztottLabirintus
ablakban a labirintus logolja ezt az éppen bejelentkezett szörny klienst.
...
Idő:325 hős:1 kincs:2 szörny:1
Idő:326 hős:1 kincs:2 szörny:1
LABIRINTUS> Belépett egy szörny.
Idő:327 hős:1 kincs:2 szörny:2
Idő:328 hős:1 kincs:2 szörny:2
...
Még továbbra is a 172.21.0.17 gépen maradva, egy újabb parancsablakból léptessünk be most egy hőst!
C:\...\Munkakönyvtár> java javattanitok.ElosztottKliens hős
HŐS> ELkészültem: IOR:000000000000002e49444c3a6a6176617474616e69746f6b2f656c6f73
7a746f74742f6f626a656b74756d6f6b2f486f733a312e3000000000000001000000000000008600
0102000000000e3137322e31372e3133352e353300055d00000031afabcb0000000020ffebeff400
Page 276
Java esettanulmányok
248 Created by XMLmind XSL-FO Converter.
000001000000000000000100000008526f6f74504f41000000000800000001000000001400000000
00000200000001000000200000000000010001000000020501000100010020000101090000000100
01010000000026000000020002
SZEREPLŐ> Beállították a pozíciómat.
Idő:359 hős:2 kincs:2 szörny:2
SZEREPLŐ> Lekérdezték a pozíciómat.
...
Interoperable Object Reference
A referenciát gondosan összehasonlítva az előzővel, a harmadik sor környékén látható a különbség.
A hős belépésének megfelelően a szerver oldal, azaz a 172.21.0.152 gép, a Parancssor - java
javattanitok.ElosztottLabirintus ablakban a labirintus logolja ezt az éppen bejelentkezett hős
objektumot.
...
Idő:357 hős:1 kincs:2 szörny:2
Idő:358 hős:1 kincs:2 szörny:2
LABIRINTUS> Belépett egy hős: .IOR:000000000000002e49444c3a6a6176617474616e69746
f6b2f656c6f737a746f74742f6f626a656b74756d6f6b2f486f733a312e300000000000000100000
00000000086000102000000000e3137322e31372e3133352e353300055d00000031afabcb0000000
020ffebeff400000001000000000000000100000008526f6f74504f4100000000080000000100000
00014000000000000020000000100000020000000000001000100000002050100010001002000010
109000000010001010000000026000000020002
Idő:359 hős:2 kincs:2 szörny:2
Idő:360 hős:2 kincs:2 szörny:2
...
Beléptethetünk további szereplőket, vagy a hősökkel ki is léphetünk a játékból. A játék programja ezt a legtöbb
esetben tolerálja:
...
Idő:369 hős:2 kincs:3 szörny:2
Idő:370 hős:2 kincs:3 szörny:2
LABIRINTUS> Egy hőst eltávolítottam.
Idő:371 hős:1 kincs:3 szörny:2
Idő:372 hős:1 kincs:3 szörny:2
...
de könnyen előfordulhatnak kivételek, amikor már nem létező CORBA objektumoknak akarunk üzenetet
küldeni.
Page 277
249 Created by XMLmind XSL-FO Converter.
4. fejezet - Példaprogramok
1. A csomagok szervezése
Jelen fejezetben összefoglaljuk az esettanulmányok osztályait. Az előző fejezetben bemutattuk ezeknek az
osztályoknak a - megjegyzésekkel gazdagon ellátott - forráskódjait. Az előzőt megelőző fejezetek ezekből a
forrásokból tartalmaznak kiragadott és sokszor leegyszerűsített forráskód részleteket. Illetve ebben a fejezetben
találjuk a labirintus példák olyan forrásait, amit eddig még nem közöltünk, például a LabirintusAlkalmazás
forrását.
1.1. A példaprogramok forrásainak letöltése
A könyvhöz készített források használatára azt javasoljuk, hogy az Olvasó a kézikönyv böngészhető
változatából jelölje ki a kívánt kódot majd - követve a kód mellett szereplő, erre vonatkozó instrukcióinkat -
illessze be a megfelelő forrásállományba! Így kerülhetők el legnagyobb valószínűséggel a különböző
platformokon (például Windows vagy Linux) való felhasználás során esetlegesen jelentkező karakterkódolási
problémák. De jelen javaslatunk ellenére egy zip tömörített állományban is letölthetővé tettük a megfelelő
csomagokba szervezett forrásokat. Ezt a javat_tanitok_forrasok.zip állományt - a kézikönyv böngészhető
változatában - a következő linket követve érheti el a kedves Olvasó:
• javat_tanitok_forrasok.zip
A javat_tanitok_forrasok.zip tömör állományba gyűjtött források elkészítésénél úgy jártunk el, hogy a
forrásokat magából a kézikönyvből másoltuk (és próbáltuk) ki, így a kedves Olvasónak nem lehet gondja a
példák kipróbálásával, ha követi a kézikönyv utasításait.
A javattanitok könyvtárban találjuk a labirintusos példákat. Ezek közül azokat gyűjtöttük ki ide a
kézikönyvből, amelyek kipróbálása a kézikönyv fő sodrában van leírva. (De például a szintén labirintusos
LabirintusAlkalmazás.java forrást nem vettük ide, az csak a kézikönyvben szerepel.
A nehany_egyeb_pelda könyvtárba pedig a kézikönyv számos egyéb példája közül csupán néhány
kiválasztottat válogattunk be. A többi esetén azt az általános elvet kövessük, hogy a forrást vágjuk ki a
kézikönyv (pl. böngészhető) változatából és a kipróbálással kövessük a kézikönyv utasításait.
Az esettanulmányokban használt ikonokat, egyszerű képeket a javat_tanitok_kepek.zip archívumba
gyűjtöttük össze, ezt hasonlóan a következő linket követve érheti el:
• javat_tanitok_kepek.zip
1.2. javattanitok.labirintus csomag
Egy labirintus mikrovilágának leírására mutatnak példát ennek a csomagnak az osztályai. A csomagban
megszervezett osztályhierarchia a következő:
java.lang.Object
javattanitok.labirintus.Labirintus
javattanitok.labirintus.GenerikusLabirintus
javattanitok.labirintus.TöbbHősösLabirintus
javattanitok.labirintus.Szereplő
javattanitok.labirintus.Hős
javattanitok.labirintus.Kincs
javattanitok.labirintus.Szörny
java.lang.Throwable (implements java.io.Serializable)
java.lang.Exception
javattanitok.labirintus.RosszLabirintusKivétel
Page 278
Példaprogramok
250 Created by XMLmind XSL-FO Converter.
1.3. javattanitok.elosztott csomag
Egy elosztott labirintus mikrovilágának leírására mutatnak példát ennek a csomagnak az osztályai. A csomagban
megszervezett osztályhierarchia a következő:
java.lang.Object
javattanitok.labirintus.Labirintus
javattanitok.labirintus.TöbbHősösLabirintus
javattanitok.elosztott.LabirintusKiszolgáló
(implements java.lang.Runnable)
javattanitok.elosztott.objektumok.SzereploPOA
javattanitok.elosztott.SzereplőKiszolgáló
javattanitok.elosztott.HősKiszolgáló
javattanitok.elosztott.KincsKiszolgáló
javattanitok.elosztott.SzörnyKiszolgáló
A CORBA objektumok hierarchiája a következő:
javattanitok.elosztott.objektumok.Szereplo
javattanitok.elosztott.objektumok.Hos
javattanitok.elosztott.objektumok.Kincs
javattanitok.elosztott.objektumok.Szorny
javattanitok.elosztott.objektumok.Labirintus
1.4. javattanitok csomag
A labirintus és az elosztott labirintus mindenféle életre keltésére példát mutató osztályokat szerveztük ebbe a
csomagba. A javattanitok.labirintus és a javattanitok.elosztott csomagok absztrahálta labirintusok
mikrovilágainak a legfőbb életre keltései következők:
1. Tiszta objektumorientált
2. Appletes
3. Teljes képernyős (Full Screen Exclusive Mode API)
4. MIDlet-es (mobiltelefonos)
5. Szervletes,
6. TCP/IP-s,
7. Java RMI-s,
8. CORBA-s
9. Elosztott
A csomagban megszervezett osztályhierarchia a következő:
java.lang.Object
java.awt.Component (implements
java.awt.image.ImageObserver, java.awt.MenuContainer,
java.io.Serializable)
java.awt.Container
java.awt.Panel (implements
Page 279
Példaprogramok
251 Created by XMLmind XSL-FO Converter.
javax.accessibility.Accessible)
java.applet.Applet
javattanitok.LabirintusApplet
(implements
java.awt.event.KeyListener)
java.awt.Window (implements
javax.accessibility.Accessible)
java.awt.Frame (implements
java.awt.MenuContainer)
javattanitok.LabirintusAlkalmazás
(implements java.awt.event.KeyListener)
javattanitok.LabirintusJáték
(implements java.lang.Runnable)
javattanitok.ElosztottKliens
javattanitok.ElosztottLabirintus
javax.microedition.lcdui.game.GameCanvas
javattanitok.LabirintusVaszon (implements java.lang.Runnable)
javattanitok.HálózatiLabirintus (implements java.lang.Runnable)
javattanitok.KorbásLabirintus
javattanitok.TávoliLabirintus (implements javattanitok.TávoliHősíthető)
javax.servlet.http.HttpServlet
javattanitok.LabirintusServlet
javattanitok.KorbásKliens
javattanitok.LabirintusKiszolgálóSzál (implements java.lang.Runnable)
javattanitok.LabirintusVilág (implements java.lang.Runnable)
javattanitok.GenerikusLabirintusVilág
javax.microedition.midlet.MIDlet
javattanitok.LabirintusMIDlet
javattanitok.korbas.TavoliHosPOA
javattanitok.TávoliHősKiszolgáló
javattanitok.TávoliKliens
java.rmi.Remote
javattanitok.TávoliHősíthető
1.4.1. A LabirintusAlkalmazás osztály
Az alábbi LabirintusAlkalmazás osztály kódját A Musztáng című pontban tárgyaljuk részletesen.
/*
* LabirintusAlkalmazás.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
package javattanitok;
import java.awt.event.ActionEvent;
import javattanitok.labirintus.*;
/**
* A labirintus csomag absztrahálta labirintus mikrovilágának egy
* alkalmazásbeli életre keltésére ad példát ez az osztály, miközben
* bemutatja a Java 6 (Musztáng) néhány újdonságát.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
* @see javattanitok.LabirintusVilág
* @see javattanitok.LabirintusJáték
* @see javattanitok.LabirintusMIDlet
* @see javattanitok.LabirintusServlet
* @see javattanitok.HálózatiLabirintus
* @see javattanitok.TávoliLabirintus
* @see javattanitok.ElosztottLabirintus
*/
public class LabirintusAlkalmazás extends java.awt.Frame
Page 280
Példaprogramok
252 Created by XMLmind XSL-FO Converter.
implements java.awt.event.KeyListener {
/** A labirintus. */
Labirintus labirintus;
/** A hős. */
Hős hős;
/** A játék vége után már nem veszünk át inputot a játékostól,
* illetve a játék világának állapota sem változik. */
boolean játékVége = false;
/** A játék vége után megjelenő üzenet. */
String végeÜzenet = "Vége a játéknak!";
// A labirintus szereplőihez rendelt képek
java.awt.Image falKép;
java.awt.Image járatKép;
java.awt.Image hősKép;
java.awt.Image szörnyKép;
java.awt.Image kincsKép;
/** Konstruktor.
*/
public LabirintusAlkalmazás() {
super("Labirintus alkalmazás");
/* itt kezdenénk, de most majd lejjebb
java.awt.SplashScreen indítóKépernyő
= java.awt.SplashScreen.getSplashScreen();
java.awt.Graphics2D g = indítóKépernyő.createGraphics(); ...
*/
labirintus = new Labirintus(6, 3);
hős = new Hős(labirintus);
hős.sor(9);
hős.oszlop(0);
falKép = new javax.swing.ImageIcon
("fal.png").getImage();
járatKép = new javax.swing.ImageIcon
("járat.png").getImage();
hősKép = new javax.swing.ImageIcon
("hős.png").getImage();
szörnyKép = new javax.swing.ImageIcon
("szörny.png").getImage();
kincsKép = new javax.swing.ImageIcon
("kincs.png").getImage();
// Ha a rendszerben támogatott az értesítési terület
if (java.awt.SystemTray.isSupported()) {
// akkor egy kis 16x16 pixel méretű
java.awt.Image ikonKép = new javax.swing.ImageIcon
("ikon.png").getImage();
// értesítő ikont
java.awt.TrayIcon értesítőIkon =
new java.awt.TrayIcon(ikonKép,
"Javat tanítok labirintus");
// most mi is kiteszünk
try {
java.awt.SystemTray.getSystemTray().
add(értesítőIkon);
} catch (java.awt.AWTException e) {
System.out.println("Értesítő ikon kitétele: "
+ e);
}
// amire ha duplán kattintunk, akkor
értesítőIkon.addActionListener(
new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
// például szorult helyzetéből kimenekítve a
// hőst, visszadobjuk a kiindulási pozíciójába
hős.sor(9);
hős.oszlop(0);
repaint();
}
});
}
Page 281
Példaprogramok
253 Created by XMLmind XSL-FO Converter.
// amit be is lehet csukni
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
setVisible(false);
System.exit(0);
}
});
addKeyListener(this);
setSize(1024, 768);
// Hozzáférünk az indító képernyőhöz
java.awt.SplashScreen indítóKépernyő
= java.awt.SplashScreen.getSplashScreen();
// Ha sikerül, azaz, ha java -splash:kép állomány neve
// formában indítottuk és még látszik a kép
if(indítóKépernyő != null) {
// akkor rajzolni fogunk rá
java.awt.Graphics2D g = indítóKépernyő.createGraphics();
// némi szöveget, amit akár a rajzolóprogrammal is
// ráírhattunk volna
java.awt.Font font1 = new java.awt.Font("Sans",
java.awt.Font.BOLD, 20);
java.awt.Font font2 = new java.awt.Font("Monospaced",
java.awt.Font.BOLD, 12);
g.setFont(font1);
// világosabb sárgával
g.setColor(new java.awt.Color(250, 255, 138));
g.drawString("Javat tanítok labirintus", 8, 240);
g.setFont(font2);
g.drawString("Bátfai Norbert", 10, 265);
g.drawString("[email protected] ", 10, 280);
// és rajzolunk egy a konkrét kép méretéhez igazított
// progress bar szerűséget (kommentben az indító
// képernyő méretéhez relatív adatok)
int sz = 382; // indítóKépernyő.getSize().width-10;
int barSzélesség = sz/10;
int m = 10; // indítóKépernyő.getSize().height;
int barMagasság = 6;
// itt szimuláljuk a játék időigényes dolgainak
// betöltését, 10 x fél másodperig altatjuk majd
// a szálunkat
for(int i = 0; i<10; ++i) {
// és ennek megfelelően frissítjük mit
// mutasson a progress bar-unk
g.setColor(java.awt.Color.WHITE);
g.drawRect(8, 285, //5, m-20,
10*barSzélesség, barMagasság);
g.fillRect(8, 285, //5, m-20,
10*barSzélesség, barMagasság);
g.setColor(java.awt.Color.YELLOW);
g.drawRect(8, 285, //5, m-20,
10*barSzélesség, barMagasság);
g.fillRect(8, 285, //5, m-20,
(i+1)*barSzélesség, barMagasság);
indítóKépernyő.update();
try {
// a fél másodperces altatás
// ez szimulálja pl., hogy töltjük be
// a már hatalmas labirintus játékunk
// képerőforrásait, készítjük a megfelelő
// adatszerkezeteket stb.
Thread.sleep(500);
} catch(Exception e) {}
}
}
setVisible(true);
}
/**
* A játékostól (aki a játék világában a hős) jövő input feldolgozása:
* a hős mozgatása a KURZOR billenytűkkel, illetve a játék világának
Page 282
Példaprogramok
254 Created by XMLmind XSL-FO Converter.
* állapot változásait is innen irányítjuk.
*/
public void keyPressed(java.awt.event.KeyEvent billentyűEsemény) {
// Mit nyomott le?
int billentyű = billentyűEsemény.getKeyCode();
if(!játékVége) {
// Merre lép a hős?
switch(billentyű) {
// A KURZOR billentyűkkel foglalkozunk, a megfelelő irányba
// lépünk
case java.awt.event.KeyEvent.VK_UP:
hős.lépFöl();
break;
case java.awt.event.KeyEvent.VK_DOWN:
hős.lépLe();
break;
case java.awt.event.KeyEvent.VK_RIGHT:
hős.lépJobbra();
break;
case java.awt.event.KeyEvent.VK_LEFT:
hős.lépBalra();
break;
}
// A játék világának állapot változása: azaz a játék többi
// szereplője is lép. Ha ezzel a lépéssel a játék világában
// történt valami lényeges: pl. vége a játéknak vagy egy szörny
// elkapta a hőst, akkor reagálunk:
switch(labirintus.bolyong(hős)) {
case Labirintus.JÁTÉK_MEGY_HŐS_RENDBEN:
break;
case Labirintus.JÁTÉK_MEGY_MEGHALT_HŐS:
// Még van élete, visszatesszük a kezdő pozícióra
hős.sor(9);
hős.oszlop(0);
break;
case Labirintus.JÁTÉK_VÉGE_MINDEN_KINCS_MEGVAN:
végeÜzenet = "Győztél, vége a játéknak!";
játékVége = true;
break;
case Labirintus.JÁTÉK_VÉGE_MEGHALT_HŐS:
végeÜzenet = "Vesztettél, vége a játéknak!";
játékVége = true;
break;
}
// Amíg nincs vége a játéknak, újra rajzoljuk a
// játék felületét, hogy látszódjanak a játék állapotában
// bekövetkezett változások
repaint();
}
}
/**
* A KeyListener számunkra most érdektelen további metódusait üres
* testtel definiáljuk felül.
*/
public void keyTyped(java.awt.event.KeyEvent billentyűEsemény) {}
public void keyReleased(java.awt.event.KeyEvent billentyűEsemény) {}
/**
* Kirajzolja a játék felületét, azaz a labirintust és a benne szereplőket:
* a hőst, a kincseket és a szörnyeket.
*/
public void paint(java.awt.Graphics g) {
// A labirintus kirajzolása
for(int i=0; i<labirintus.szélesség(); ++i) {
for(int j=0; j<labirintus.magasság(); ++j) {
if(labirintus.szerkezet()[j][i])
g.drawImage(falKép, i*falKép.getWidth(this),
Page 283
Példaprogramok
255 Created by XMLmind XSL-FO Converter.
j*falKép.getHeight(this), null);
else
g.drawImage(járatKép, i*járatKép.getWidth(this),
j*járatKép.getHeight(this), null);
}
}
// A kincsek kirajzolása
Kincs[] kincsek = labirintus.kincsek();
for(int i=0; i<kincsek.length; ++i) {
g.drawImage(kincsKép,
kincsek[i].oszlop()*kincsKép.getWidth(this),
kincsek[i].sor()*kincsKép.getHeight(this), null);
// Ha már megvan a kics, akkor áthúzzuk
if(kincsek[i].megtalálva()) {
g.setColor(java.awt.Color.red);
g.drawLine(kincsek[i].oszlop()*kincsKép.getWidth(this),
kincsek[i].sor()*kincsKép.getHeight(this),
kincsek[i].oszlop()*kincsKép.getWidth(this)
+ kincsKép.getWidth(this),
kincsek[i].sor()*kincsKép.getHeight(this)
+ kincsKép.getHeight(this));
g.drawLine(kincsek[i].oszlop()*kincsKép.getWidth(this)
+ kincsKép.getWidth(this),
kincsek[i].sor()*kincsKép.getHeight(this),
kincsek[i].oszlop()*kincsKép.getWidth(this),
kincsek[i].sor()*kincsKép.getHeight(this)
+ kincsKép.getHeight(this));
} else {
// ellenkező esetben kiírjuk az értékét
g.setColor(java.awt.Color.yellow);
g.drawString(""+kincsek[i].érték(),
kincsek[i].oszlop()*kincsKép.getWidth(this)
+ kincsKép.getWidth(this)/2,
kincsek[i].sor()*kincsKép.getHeight(this)
+ kincsKép.getHeight(this)/2);
}
}
// A szörnyek kirajzolása
Szörny[] szörnyek = labirintus.szörnyek();
for(int i=0; i<szörnyek.length; ++i)
g.drawImage(szörnyKép,
szörnyek[i].oszlop()*szörnyKép.getWidth(this),
szörnyek[i].sor()*szörnyKép.getHeight(this), null);
// A hős kirajzolása
g.drawImage(hősKép,
hős.oszlop()*hősKép.getWidth(this),
hős.sor()*hősKép.getHeight(this), null);
// A játék aktuális adataiból néhány kiíratása
g.setColor(java.awt.Color.black);
g.drawString("Életek száma: "+hős.életek(), 10, 40);
g.drawString("Gyűjtött érték: "+hős.pontszám(), 10, 60);
if(játékVége) {
g.setColor(java.awt.Color.black);
g.drawString(végeÜzenet, 420, 350);
}
}
/**
* A játék felületének kirajzolásakor ne legyen villogás, ezért
* az eredeti, a felület törlését elvégző update metódust felüldefiniáljuk.
*/
public void update(java.awt.Graphics g) {
paint(g);
}
/**
* A program alkalmazásként való indíthatóságát szolgálja.
*/
Page 284
Példaprogramok
256 Created by XMLmind XSL-FO Converter.
public static void main(String[] args) {
// Ha itt van a vezérlés, akkor nem igaz az, hogy appletként indítottuk
new LabirintusAlkalmazás();
}
}
1.4.1.1. A LabirintusAlkalmazás kipróbálása
A Munkakönyvtár nevű munkakönyvtárunkba másoljuk be a 16x16 vagy 32x32 pixeles ikon.png ikont és A
Musztáng, a Java 6 újdonságait bemutató pontban részletesen tárgyalt inditokep.png indítóképet. Majd -
feltéve, hogy a labirintus API-t a korábbi pontokban már felélesztettük - fordíthatjuk és futtathatjuk az alábbiak
szerint:
$ javac javattanitok/LabirintusAlkalmazás.java
$ java -splash:inditokep.png javattanitok.LabirintusAlkalmazás
A futásról pillanatfelvételeket ugyancsak A Musztáng című pontban talál a kedves Olvasó.
A hős szorult helyzetben
A LabirintusAlkalmazás osztály az értesítési területen elhelyezett ikonon való kattintási
eseményeket is feldolgozza: példaként a szorult helyzetbe került hőst teleportálja a kezdeti pozícióba:
// amire ha duplán kattintunk, akkor
értesítőIkon.addActionListener(
new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
// például szorult helyzetéből kimenekítve a
// hőst, visszadobjuk a kiindulási pozíciójába
hős.sor(9);
hős.oszlop(0);
repaint();
}
});
Az API doksit figyelmesen tanulmányozó Olvasó a java.awt.TrayIcon osztály leírásánál láthatja,
hogy a jelen példán túl, az értesítési ikonhoz egy előugró menü is rendelhető.
Page 285
257 Created by XMLmind XSL-FO Converter.
5. fejezet - A példák kipróbálása
1. A Java telepítése gépünkre
1.1. A Java SE, Java ME fejlesztői csomag letöltése és beállítása
Mindig a legfrissebb verziójú Java SE, ME fejlesztői csomagokat használjuk. A Java telepítését az aktuális
Musztáng verzióval mutatjuk be. A Musztáng verzió teljes és hivatalos neve: Java Platform, Standard Edition 6,
röviden Java SE 6. Az ennek a platformnak megfelelő Java fejlesztői csomag a Java SE Development Kit 6,
röviden a JDK 6. Ezt kell letöltenünk, ha fejleszteni akarunk, márpedig mi fejleszteni akarunk. A Java
programok futtatásához a Java SE Runtime Environment 6 - Java Futtató Környezet, JRE 6 is elegendő, de ez
nem ad eszközöket, azaz lehetőséget a Java programozásra. Természetesen a JDK 6 használata esetén nincs
szükségünk a JRE-re.
Alapvetően két lehetőségünk van: vagy parancssorban dolgozunk, vagy egy integrált fejlesztői környezetet
használunk Java programjaink írásához. Az előbbi lehetőség bemutatásával kezdjük, de mindenkinek a
másodikat, a fejlesztői környezet használatát ajánljuk. Nemcsak a programozás kényelmi, hanem praktikus
szempontjai miatt is. Ezekre a NetBeans fejlesztői környezet telepítésének leírásakor majd rámutatunk.
Megjegyezhetjük, hogy a NetBeans letöltése kapcsán van olyan opciónk is, hogy egy olyan NetBeans IDE-t
töltünk le, ami mellé be van téve egy komplett JDK is, erről a kombinált lehetőségről írunk majd a NetBeans
IDE 5.5 című pontban.
1.1.1. Java SE Linux környezetben
Keressük fel a SUN Java fejlesztőknek szánt webhelyét: http://java.sun.com, itt néhány kattintás után a
„Downloads” alatt megtaláljuk a JDK 6-ot, vagy akár eggyel is a lap bal oldalának „Popular Downloads”
ablakában, a „Java SE 6 Beta 2” link választásával a http://java.sun.com/javase/downloads/ea.jsp korai
hozzáférés (mert a Musztáng még éppen béta) lapra jutunk, ahol a „JDK 6 Beta 2” letöltését választjuk.
A letöltési feltételek elfogadásának bekattintása után letölthetjük a mi gépünkre szánt Javat. Mivel mi most
éppen egy 64 bites Linuxon (egy Fedora Core 5 Linux operációs rendszerrel futó Sun W1100z Workstation
gépen akarunk dolgozni, ami már egy 64 bites rendszer) így a „Linux x64 Platform - Java(TM) SE Development
Kit 6 Beta 2” alól Linux környezetre a „Linux x64 in self-extracting file”, jdk-6-beta2-linux-amd64.bin
letöltését választjuk.
Tegyük fel, hogy a jdk-6-beta2-linux-amd64.bin állományt a /home/norbi/Java könyvtárba mentettük,
ekkor futási jogot adunk a letöltött állományra, majd lefuttatjuk.
[norbi@niobe Java]$ ls -l
total 48740
-rw-r--r-- 1 norbi norbi 49854400 Sep 9 11:32 jdk-6-beta2-linux-amd64.bin
[norbi@niobe Java]$ chmod +x jdk-6-beta2-linux-amd64.bin
[norbi@niobe Java]$ ./jdk-6-beta2-linux-amd64.bin
Lelkes SPACE billentyű nyomkodás, majd a yes begépelése után majdnem készen is vagyunk. Még azt kell
közölnünk a rendszerrel, hogy hol van az éppen most kicsomagolt Java: mindig a kicsomagolt
/home/norbi/Java/jdk1.6.0 könyvtár bin könyvtárával kell bővíteni az elérési utat
[norbi@niobe Java]$ export PATH=/home/norbi/Java/jdk1.6.0/bin:$PATH
[norbi@niobe Java]$ java -version
java version "1.6.0-beta2"
Java(TM) SE Runtime Environment (build 1.6.0-beta2-b86)
Java HotSpot(TM) 64-Bit Server VM (build 1.6.0-beta2-b86, mixed mode)
Page 286
A példák kipróbálása
258 Created by XMLmind XSL-FO Converter.
igen, ez már a mi frissen feltett 6-os Javank.
Java kényelmesen
Ha nem akarjuk a fenti PATH beállítást minden olyan ablakban külön kiadni, ahol Javazni szeretnénk,
akkor ezt a parancsot érdemes bevenni egyszerű felhasználóként a .bash_profile állományunkba,
vagy esetleg rendszergazdaként a /etc/profile állományba. Például ezt írjuk az említett állományok
valamelyikének végére:
export PATH=/home/norbi/Java/jdk1.6.0/bin:$PATH
ezután, az újbóli bejelentkezésekkor már bármely kinyitott parancsablakból látszanak majd a Javas
parancsok, például a javac, a fordítót indító, vagy a java, a Java Virtuális Gépet indító parancs, az idlj,
az IDL fordítót, vagy az orbd, az ORB szoftvert indító parancsok.
1.1.2. Java SE Windows környezetben
Keressük fel a SUN Java fejlesztőknek szánt webhelyét: http://java.sun.com, itt néhány kattintás után a
„Downloads” alatt megtaláljuk a JDK 6-ot, vagy akár eggyel is a lap bal oldalának „Popular Downloads”
ablakában, a „Java SE 6 Beta 2” link választásával a http://java.sun.com/javase/downloads/ea.jsp korai
hozzáférés (mert a Musztáng még éppen béta) lapra jutunk, ahol a „JDK 6 Beta 2” letöltését választjuk.
A letöltési feltételek elfogadásának bekattintása után letölthetjük a mi gépünkre szánt Javat. Most éppen egy
Windowsra, így a „Windows Platform - Java(TM) SE Development Kit 6 Beta 2” Windows környezetre a
„Windows Offline Installation, Multi-language”, jdk-6-beta2-windows-i586.exe letöltését választjuk.
A letöltés után a letöltött exe állományt lefuttatjuk. Tegyük fel, hogy e futtatáskor a c:\Program
Files\Java\jdk1.6.0 könyvtárba telepítettük a Javat. Ha (Start/Kellékek/)Parancssor-ból akarjuk használni a
Javat, akkor még azt kell közölnünk a rendszerrel, hogy hol van az éppen most telepített Java: mindig a
kicsomagolt c:\Program Files\Java\jdk1.6.0 könyvtár bin könyvtárával kell bővíteni az elérési utat
C:\...> set PATH="c:\Program Files\Java\jdk1.6.0\bin";%PATH%
C:\Documents and Settings\norbi> java -version
java version "1.6.0-beta2"
Java(TM) SE Runtime Environment (build 1.6.0-beta2-b86)
Java HotSpot(TM) Client VM (build 1.6.0-beta2-b86, mixed mode, sharing)
igen, ez már a mi frissen feltett 6-os Javank.
Ha nem akarjuk a fenti PATH beállítást minden olyan ablakban külön kiadni, ahol Javazni szeretnénk, akkor a
PATH bővítését érdemes bevenni a Start/Vezérlőpult/Teljesítmény és karbantartás/ Rendszer/Speciális
fül/Környezeti változók gomb kattintása után a felhasználói vagy a rendszerváltozók közé.
Java 6
A kézikönyv írása végén vált elérhetővé a - béta2 és az RC után a - stabil Java 6.
Keressük fel a SUN Javas webhelyét: http://java.sun.com, itt néhány kattintás után (a „Downloads”
menüpont alatt a „Java SE” link választásával) a http://java.sun.com/javase/downloads/index.jsp, a
„Java SE Downloads” című lapra jutunk, ahonnan letölthetjük a parancssoros „JDK 6”-ot vagy a
NetBeans fejlesztői környezettel együtt elérhető „JDK 6 with NetBeans 5.5” név alatt belinkelt JDK-t.
A feltételek elfogadásának beixelése után letölthetjük a kívánt platformhoz (operációs rendszerhez)
tartozó Javat.
1.1.3. A Java dokumentáció, azaz az API doksi letöltése
Page 287
A példák kipróbálása
259 Created by XMLmind XSL-FO Converter.
A fejlesztői csomag letöltésénél arra figyeltünk, hogy mindig a legfrissebb verziójú Java SE, ME fejlesztői
csomagokat használjuk. A dokumentációnál pedig arra kell tekintettel lennünk, hogy megfeleljen a használt
csomag verziójának. Ez nem nehéz, mert azon a lapon, ahonnan a fejlesztői csomagot letöltjük, megtaláljuk a
dokumentációt is.
Keressük fel a SUN webhelyét: http://java.sun.com, itt néhány kattintás után a „Downloads” alatt megtaláljuk a
JDK 6-ot, vagy akár eggyel is a lap bal oldalának „Popular Downloads” ablakában, a „Java SE 6 Beta 2” link
választásával a http://java.sun.com/javase/downloads/ea.jsp korai hozzáférés (mert a Musztáng még éppen béta)
lapra jutunk, ahol a „Java SE 6 Beta 2 Documentation” letöltését választjuk.
A letöltési feltételek elfogadásának bekattintása után letölthetjük a természetesen platformfüggetlen
dokumentációt. „Platform - Java(TM) SE Development Kit Documentation 6 Beta 2” Linux környezetre a „Java
SE Development Kit Documentation 6 Beta 2”, jdk-6-beta2-doc.zip letöltését választjuk.
Ezt a letöltött állományt kicsomagolva megtaláljuk benne a HTML dokumentációt, azaz egy böngészővel tudjuk
nézegetni. Ha az api könyvtárának index.html állományát kinyitjuk, akkor máris látjuk a szokásos három
frame-be szervezett Java SE API dokumentációt.
A SUN Javas http://java.sun.com webhelyén természetesen böngészhetjük on-line is a Java ME, SE vagy EE
dokumentációt. Illetve arra hívjuk még fel a figyelmet, hogy a NetBeans Mobility telepítése után a Java ME API
dokumentációt a netbeans-5.5\mobility7.3\emulators-inst\wtk22_win.zip és ezen a zip állományon
belül belül az emulator\wtk22\docs\api\midp könyvtárban is megtaláljuk.
1.1.4. Java ME
A Java ME beállításának egyik módja a Sun Java Wireless Toolkit letöltése, de mi inkább a NetBeans
környezeten belüli Netbeans Mobility használatát javasoljuk a mobilos fejlesztésekhez. Számos okból, amiket
majd a következő, a NetBeans beállításáról szóló részben bontunk ki, miközben kitérünk a Netbeans Mobility
letöltésére.
A Sun Java Wireless Toolkit letöltése kapcsán keressük fel a SUN webhelyét: http://java.sun.com, itt a
„Downloads” alatt a „Java ME” linket követve a http://java.sun.com/javame/downloads/index.jsp lapra jutunk,
ahonnan mindig a legfrissebb, most éppen a „Sun Java Wireless Toolkit 2.5 for CLDC, Beta” csomagot töltsük
le.
Külön állományt kell letöltenünk attól függően, hogy Linux vagy Windows környezetben akarunk-e dolgozni.
Mindkét esetben a Java SE telepítéséhez hasonlóan kell eljárnunk, azaz futtatni kell a letöltött állományt. De itt
nincs szükség az elérési út (azaz a PATH környezeti változó) állítására.
1.1.5. A NetBeans integrált fejlesztői környezet letöltése és használata
„Egy jó ideje már, hogy áttértem a NetBeans IDE használatára.” — James Gosling [GOSLING INTERJÚ]
A NetBeans integrált fejlesztői környezetet (NetBeans IDE) a http://www.netbeans.org/ webhelyről tölthetjük
le. De a NetBeans IDE letöltés be szokott lenni linkelve a SUN Javas http://java.sun.com webhelyére is, a lap
bal oldalának „Popular Downloads” ablakába. Itt a „NetBeans IDE” linket választva a
http://www.netbeans.org/downloads/index.html lapra kerülünk. Ha itt a „NetBeans IDE, Mobility Pack and
Profiler 5.0 downloads” 5.0 verziójú NetBeans IDE-t választjuk, akkor a
http://www.netbeans.info/downloads/download.php?type=5.0 lapra kerülve a kívánt operációs rendszer
kiválasztása után a „NetBeans IDE 5.5 Beta 2 Installer”-t és a „NetBeans Mobility Pack 5.0 Installer”-t töltsük
le. Mindkettő futtatható, nincs más dolgunk, mint (a fenti sorendben) lefuttatni őket. De inkább a már elérhető
5.5 változatot töltsük le! A http://www.netbeans.org/downloads/index.html lapon a „NetBeans IDE 5.5 Beta 2”
választásával, a http://www.netbeans.info/downloads/download.php?type=5.5b2 lapra kerülve a kívánt
operációs rendszer kiválasztása után a „NetBeans IDE 5.5 Beta 2 Installer”-t és a „NetBeans Mobility 5.5 Beta 2
Installer”-t töltsük le.
A NetBeans letöltése kapcsán van olyan opciónk is, hogy egy olyan NetBeans IDE-t töltünk le, ami mellé be
van téve egy komplett JDK is, erről a kombinált lehetőségről írunk majd a következő NetBeans IDE 5.5 című
pontban.
Page 288
A példák kipróbálása
260 Created by XMLmind XSL-FO Converter.
Miért válasszuk a fejlesztéshez a NetBeans IDE-t a parancssoros fejlesztéssel szemben? Számos okból.
Ismertessünk ezek közül néhányat:
• a fordítási hibák figyelése és jelzése a szövegszerkesztőben folyamatos
• a szövegszerkesztő adja a szokásos kényelmi szolgáltatásokat, mint például a csomag és osztálynevek,
példány és metódusnevek automatikus beírása
• szervletek írásánál könnyű tesztelési (menüből kattintható) lehetőséget biztosít
• mobil fejlesztéseknél különösen fontos, hogy az IDE tartalmazzon egy megfelelő verziójú obfuszkátort, amit
így nem kell külön letölteni és beállítani.
1.1.6. A NetBeans IDE 5.5
A kézikönyv írása közben vált elérhetővé a NetBeans IDE 5.5 immár nem béta verziója. Ezt a
http://java.sun.com/javase/downloads/index.jsp lapon a „JDK 5.0 Update 9 with NetBeans 5.5” linket követve,
vagy közvetlenül a http://java.sun.com/j2se/1.5.0/download-netbeans.html, a „Download J2SE Development Kit
5.0 Update 9 with NetBeans IDE 5.5 Bundle” lapról tölthetjük le. Ennek az érdekessége, mint ahogyan az a link
nevéből is kiderül, hogy nemcsak a NetBeans környezetet, hanem egyben a megfelelő JDK 5 csomagot is
letölthetjük. A linket követve Linux alá a „J2SE(TM) and NetBeans(TM) IDE Cobundle (J2SE 1.5.0 U9 / NB
5.5) Linux”, a jdk-1_5_0_09-nb-5_5-linux.bin 114.40 megás állományt töltsük le. Windows alá pedig a
„J2SE(TM) and NetBeans(TM) IDE Cobundle (J2SE 1.5.0 U9 / NB 5.5) Windows”, a jdk-1_5_0_09-nb-5_5-
win.exe 114.27 megás állományt.
Az 5.5 IDE környezetnek megfelelő NetBeans Mobility csomagot a
http://www.netbeans.info/downloads/index.php lap „NetBeans Mobility Pack 5.5 Download” linkjét követve
érhetjük el.
2. A példaprogramok futtatásáról általában
A példák teljes kódját közöljük a kézikönyvben. Tipikusan a közlés helyén példát mutatunk a programok
felélesztésére és futtatására is. Általában annyit mondhatunk, hogy - tapasztalataink szerint - az Olvasónak a
példák kipróbálásával kapcsolatban az a jellemző problémája, hogy a közölt forráskódrészleteket nem kellő
gondossággal jelöli ki és illeszti be kedvenc szövegszerkesztőjébe, amiből fordítási hibák következnek.
Tipikusan ilyen, hogy a több oldalas programból csak részeket jelöl ki Olvasó, vagy lehagyja néhány sor végét a
kijelölésből. Illetve, ha a kijelölés a pdf olvasóban történik, akkor az olvasótól és a beállításaitól függően akár
oldalszámok is lehetnek a kijelölésben, amik a program szövegében idegen elemek, így természetesen fordítási
hibát okoznak. Ezért javasoljuk, hogy a kézikönyv böngészhető változatából másolja ki az Olvasó a
forráskódokat.
Fordítás Linux alatt
Kérdés: Linux (Fedora Core 5) alatt nem tudom lefordítani az ékezetes betűket tartalmazó osztályokat.
Válasz: Rendszergazdaként a /etc/sysconfig/i18n állományban állítsuk magyarra a lokális
környezetet! Tehát a /etc/sysconfig/i18n állomány a következőket tartalmazza:
LANG="hu_HU.ISO-8859-2"
LC_ALL="hu_HU.ISO-8859-2"
Kérdés: Linux (Fedora Core 6) alatt nem tudom lefordítani az ékezetes betűket tartalmazó osztályokat.
Válasz: Ha maguk az állomány nevek rendben vannak, mert mondjuk eleve Linux alatt készítettük el
őket, akkor elegendő a fordításnál az encoding kapcsoló használata, például:
Page 289
A példák kipróbálása
261 Created by XMLmind XSL-FO Converter.
$ javac -encoding ISO-8859-2 javattanitok/LabirintusAlkalmazás.java
Page 290
262 Created by XMLmind XSL-FO Converter.
A. függelék - Java mellékletek
1. A Java verziók újdonságai a tigristől a delfinig
Most részletesebben kibontjuk a Java történelemről szóló korábbi részt abban az esetben, amikor időtengelyen
előre haladunk: a közeli múlt (2004 vége, 2005 eleje) a Java 5, a Tiger. A jelen (2006 nyara - 2007 tavasza) a
Java 6, a Mustang. A közeli jövő (2008 második fele) a Java 7, a Dolphin. A következő három pontban
mindhárom verzió esetén megnézünk néhány szívdobogtató finomságot.
1.1. A tigris
A Java platformnak ez a verziója már a közeli múlt, így részletesebben nem is foglalkozunk vele, hanem csak az
általa bevezetett újdonságokat említjük röviden. Ezt viszont meg kell tennünk, mert nagy dolgok történtek ennek
a verziónak a bevezetésével.
A Java Tigris verziója nemcsak az API világában hozott újdonságokat, maga a nyelv is új elemekkel bővült:
i. megjelent a generikus,
ii. egy újfajta for ciklus: az iteráló ciklus,
iii. immár a primitív Java típusok automatikusan csomagolódnak be és vissza csomagoló osztályaikba,
iv. megjelent a felsorolásos típus,
v. lehetőség nyílt változó paraméterszámú függvények írására,
vi. és statikus tagok olyan importjára, ami elhagyhatóvá teszi a tagra vonatkozó osztálynév minősítést.
A generikus használatával olyan új típusokat (osztályokat) hozhatunk létre, melyek még maguknál a típusoknál
(osztályoknál) is általánosabb szintet hoznak programunk világába. Mert a generikus típus egy olyan általános
típus, amit egy típussal lehet paraméterezni, azaz konkréttá tenni. Például ha van egy generikus (általános) lista
típusom, akkor annak típus paramétere az lehet, hogy ennek a listának az elemei milyen típusúak, tehát az
általános lista típusból konkretizált listám milyen típusokkal működjön majd... Vagy készíthetek olyan saját
generikus (általános) számológép típust, ami ugyanúgy teszi a dolgát: összead, szoroz, de egyszer egészekkel,
máskor törtekkel, attól függően, hogy az Integer vagy a Double típussal paraméterezve, konkretizálva
készítettem-e el. (A generikusról általános olvasmányként lásd a [PICI PROGRAMOZÁS I JEGYZET]
jegyzetet.)
A Tigris előtti Javaban a (java.util.)List objektumba bármilyen más objektumot betehettem, akár
különbözőeket is, de amikor ki akartam venni az elemeket, akkor tudnom kellett, hogy milyen típusúak. Ha
ennek kapcsán valami hiba történt, például mégsem az volt betéve a listába, amit gondolt a programozó... hát,
akkor az csak a program futása közben derült ki. A Tigristől ez a List egy generikus interfész, tehát immár egy
típussal paraméterezhető, konkretizálható lista. Ez azért jó, mert már a fordítási időben kiderül, ha nem
megfelelő típusú elemeket akarunk betenni a listába vagy kivenni abból.
Játsszunk egy kicsit a generikussal, terjesszük ki a javattanitok.labirintus.Labirintus osztályt úgy,
hogy a labirintusunk szereplői ne tömbök, hanem listák legyenek. Mi ezt a kiterjesztést a
javattanitok.labirintus.GenerikusLabirintus osztályban tettük meg. A példánytagokat bemutató
részben a Labirintus osztály
protected Kincs [] kincsek;
protected Szörny [] szörnyek;
kincseit és szörnyeit így módosítottuk a GenerikusLabirintus osztályban:
Page 291
Java mellékletek
263 Created by XMLmind XSL-FO Converter.
protected java.util.List<Kincs> kincsek;
protected java.util.List<Szörny> szörnyek;
Például a kincsek referenciát egy kincs objektumokat tartalmazó listaként deklaráljuk, az által, hogy a generikus
lista típus paramétere itt a Kincs típus, ezt jelöli a <Kincs>. Tehát a generikus listát a Kincs típussal
konkretizáltuk.
Az elkészített GenerikusLabirintus osztályt a javattanitok.GenerikusLabirintusVilág osztályban
keltettük életre. A GenerikusLabirintus osztályba bevettünk néhány olyan metódust is, aminek a labirintus
tekintetében nincs gyakorlati funkciója, de játszik kicsit a bevezetett generikus listáinkkal:
public void nyomtat1(java.util.List<?> lista) {
for(Object objektum: lista)
System.out.println(objektum);
}
public void nyomtat2(java.util.List<? extends Szereplő> lista) {
for(Szereplő szereplő: lista)
System.out.println(szereplő);
}
public <E> void nyomtat3(java.util.List<E> lista) {
for(E e: lista)
System.out.println(e);
}
az osztályt elláttuk indító main függvénnyel is, arra az esetre, ha nem a labirintusban akarjuk kipróbálni a fenti
három módszert. Mit csinálnak?
Az előző pontban a GenerikusLabirintus osztály írásakor már ezt a ciklust használtuk, külön kiemelhető az
ilyen egymásba ágyazás esetén a jó olvashatóság:
for(Szörny szörny: szörnyek)
for(Kincs kincs: kincsek)
if(kincs.megtalált(szörny))
szörny.megtaláltam(kincs);
Az iteráló ciklus egyébként tömbökre is alkalmazható, erre is példát mutat a GenerikusLabirintus azzal,
ahogyan a Labirintus
boolean vanKincs(int sor, int oszlop) {
boolean van = false;
for(int i=0; i<kincsek.length; ++i)
if(sor == kincsek[i].sor()
&& oszlop == kincsek[i].oszlop()
&& !kincsek[i].megtalálva()) {
van = true;
break;
}
return van;
}
Page 292
Java mellékletek
264 Created by XMLmind XSL-FO Converter.
módszerét felüldefiniálja:
boolean vanKincs(int sor, int oszlop) {
boolean van = false;
for(Kincs kincs: kincsek)
if(sor == kincs.sor()
&& oszlop == kincs.oszlop()
&& !kincs.megtalálva()) {
van = true;
break;
}
return van;
}
ami felüldefiniálást egyébként kénytelen is megtenni, hiszen a GenerikusLabirintus osztálynak már az új
szörny és kincs listákkal kell dolgoznia!
1.2. A Musztáng
Azaz a teljes és hivatalos nevén: Java Platform, Standard Edition 6, röviden Java SE 6. Az ennek a platformnak
megfelelő Java fejlesztői csomag a Java SE Development Kit 6, röviden a JDK 6. Ezt kell letöltenünk, ha
fejleszteni akarunk, a letöltés tekintetében lásd A Java telepítése gépünkre című pontot.
A Musztángban - szemben az előzővel, a Tigrissel - nem történtek változások magát a Java nyelvet illetően,
ellenben például a Musztáng Java OO világ gyarapodott. Ezt a gyarapodást mi a GUI kapcsán tekintjük át, ahol
több látványos újításról tudunk örömhírt adni. Részletesebben az értesítési területhez való hozzáféréssel és az
indítóképernyő használatával foglalkozunk a következő pontokban.
A Musztángtól lehetőségünk van a programunkhoz a szokásos menüvel ellátható gyorsindító ikont
(java.awt.TrayIcon) elhelyezni az értesítési területen (java.awt.SystemTray). Arra az egyszerű esetre
mutatunk példát, amikor a programunk futásakor elhelyezi ide a megfelelő ikonját és feldolgozza az ikonról
érkező legegyszerűbb eseményt: példaként tehát, ha duplán kattintunk, akkor kimenti hősünket esetleges szorult
helyzetéből és visszadobja a labirintus kezdő pontjába.
// Ha a rendszerben támogatott az értesítési terület
if (java.awt.SystemTray.isSupported()) {
// akkor egy kis 16x16 pixel méretű
java.awt.Image ikonKép = new javax.swing.ImageIcon
("ikon.png").getImage();
// értesítő ikont
java.awt.TrayIcon értesítőIkon =
new java.awt.TrayIcon(ikonKép,
"Javat tanítok labirintus");
// most mi is kiteszünk
try {
java.awt.SystemTray.getSystemTray().
add(értesítőIkon);
} catch (java.awt.AWTException e) {
System.out.println("Értesítő ikon kitétele: "
+ e);
}
// amire ha duplán kattintunk, akkor
értesítőIkon.addActionListener(
new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
// például szorult helyzetéből kimenekítve a
// hőst, visszadobjuk a kiindulási pozíciójába
hős.sor(9);
hős.oszlop(0);
Page 293
Java mellékletek
265 Created by XMLmind XSL-FO Converter.
repaint();
}
});
}
Ha programunk indulása a szokásostól több időt vesz igénybe, mert sok erőforrást, például képeket kell
betöltenie, bonyolult adatszerkezeteket, inicializálásokat elvégeznie, akkor elegáns ennek az időnek a múlását
demonstráló képernyővel informálni, szórakoztatni a felhasználót. A Mustang erre lehetőséget ad. A
legegyszerűbb módja ennek, hogy amikor a programunkat átadjuk a Java Virtuális Gépnek, akkor a -splash
kapcsolót használva az indító képernyőnek (java.awt.SplashScreen) szánt képet is átadjuk. A
következőkben példaként a LabirintusAlkalmazás programunkat ruházzuk fel ezzel a lehetőséggel.
Ezt az indító képet készítettük, alul a folyamatjelzőt (progress bar) pusztán a rajzolóprogrammal tettük rá, hogy
le tudjuk olvasni a kívánt koordinátákat.
Minél közelebb járunk ahhoz, hogy programunk befejezze az indulás kapcsán elvégzett időigényes dolgait,
annál nagyobb sárga, hosszú, lapos téglalapot rajzolunk majd. Szükségünk lesz a téglalap bal felső sarkának a
koordinátáira és a téglalap szélességére, magasságára. Ezeket a következő ábra szerint olvassuk le.
A 8. oszlop 285. sora lesz a téglalap bal felső sarka, a szélessége 382 , a magassága pedig 10 pixel.
// Hozzáférünk az indító képernyőhöz
java.awt.SplashScreen indítóKépernyő
= java.awt.SplashScreen.getSplashScreen();
// Ha sikerül, azaz, ha java -splash:kép állomány neve
// formában indítottuk és még látszik a kép
if(indítóKépernyő != null) {
// akkor rajzolni fogunk rá
java.awt.Graphics2D g = indítóKépernyő.createGraphics();
// némi szöveget, amit akár a rajzolóprogrammal is
// ráírhattunk volna
java.awt.Font font1 = new java.awt.Font("Sans",
java.awt.Font.BOLD, 20);
java.awt.Font font2 = new java.awt.Font("Monospaced",
java.awt.Font.BOLD, 12);
g.setFont(font1);
// világosabb sárgával
g.setColor(new java.awt.Color(250, 255, 138));
g.drawString("Javat tanítok labirintus", 8, 240);
Page 294
Java mellékletek
266 Created by XMLmind XSL-FO Converter.
g.setFont(font2);
g.drawString("Bátfai Norbert", 10, 265);
g.drawString("[email protected] ", 10, 280);
// és rajzolunk egy a konkrét kép méretéhez igazított
// progress bar szerűséget (kommentben az indító
// képernyő méretéhez relatív adatok)
int sz = 382; // indítóKépernyő.getSize().width-10;
int barSzélesség = sz/10;
int m = 10; // indítóKépernyő.getSize().height;
int barMagasság = 6;
// itt szimuláljuk a játék időigényes dolgainak
// betöltését, 10 x fél másodperig altatjuk majd
// a szálunkat
for(int i = 0; i<10; ++i) {
// és ennek megfelelően frissítjük mit
// mutasson a progress bar-unk
g.setColor(java.awt.Color.WHITE);
g.drawRect(8, 285, //5, m-20,
10*barSzélesség, barMagasság);
g.fillRect(8, 285, //5, m-20,
10*barSzélesség, barMagasság);
g.setColor(java.awt.Color.YELLOW);
g.drawRect(8, 285, //5, m-20,
10*barSzélesség, barMagasság);
g.fillRect(8, 285, //5, m-20,
(i+1)*barSzélesség, barMagasság);
indítóKépernyő.update();
try {
// a fél másodperces altatás
// ez szimulálja pl., hogy töltjük be
// a már hatalmas labirintus játékunk
// képerőforrásait, készítjük a megfelelő
// adatszerkezeteket stb.
Thread.sleep(500);
} catch(Exception e) {}
}
}
Végül lássuk munkánk eredményét! Például Linuxon az XFCE felület alatt nézzük meg az indítóképernyőt az
alábbi képeken.
Page 295
Java mellékletek
267 Created by XMLmind XSL-FO Converter.
Page 296
Java mellékletek
268 Created by XMLmind XSL-FO Converter.
Illetve ugyancsak Linuxon a GNOME felületet használva az indítóikont.
1.3. A delfin
A Delfint 2008 második félévére várjuk, de a http://java.sun.com lapjain előzetesen olvashatunk az
újdonságokról. A nyelv változása tekintetében a metódus referenciák bevezetését nagyon izgalmasnak tartjuk.
(Ezeket a bevezetendő metódus referenciákat például a C nyelvbeli függvény mutatókkal érezzük rokonnak.) A
programok szervezése területén a csomagszerkezet és a jar állományok újítása várható... és még sorolhatnánk a
finomságokat: a http://mustang.dev.java.net lapot figyelve lehetünk naprakészek.
2. Az első Java tapasztalatok
Ebben a mellékletben ismertetjük az egyetemünkön a levelező informatika tanárképzésbeli WWW alkalmazások
fejlesztése című tárgy első óráját. Ennek a tárgynak a példái jelen pillanatban a Java nyelv használatára épülnek.
De napjainkban, egy átmeneti állapotban, amikor a hallgatók (akik tipikusan továbbképzésként résztvevő
gyakorló pedagógusok) a képzés során már nem Pascalban programoznak, de még nem is Javaban, szükséges
némi gyakorlati Java programozás alapozás is. Ezt az alapozást ismertetjük ebben a mellékletben, mint a
programozásban egyébként járatos informatikusoknak szánt Java bevezetőt.
A következő Szerver osztály kódját másoljuk a Szerver.java állományba!
public class Szerver {
public static void main(String [] args) {
try {
java.net.ServerSocket serverSocket =
new java.net.ServerSocket(2006);
Page 297
Java mellékletek
269 Created by XMLmind XSL-FO Converter.
while(true) {
java.net.Socket socket = serverSocket.accept();
java.io.PrintWriter kimenőCsatorna =
new java.io.PrintWriter(socket.getOutputStream());
kimenőCsatorna.println("Helló, Világ!\n");
kimenőCsatorna.flush();
socket.close();
}
} catch(java.io.IOException ioE) {
ioE.printStackTrace();
}
}
}
A következő Kliens osztály kódját pedig másoljuk a Kliens.java állományba!
public class Kliens {
public static void main(String[] args) {
try {
java.net.Socket socket
= new java.net.Socket("localhost", 2006);
java.io.BufferedReader bejövőCsatorna =
new java.io.BufferedReader(
new java.io.InputStreamReader(socket.getInputStream()));
System.out.println(bejövőCsatorna.readLine());
socket.close();
} catch(java.io.IOException ioE) {
ioE.printStackTrace();
}
}
}
Most két külön ablakban lépjünk be abba a könyvtárba, ahová a fenti két forrásállományt másoltuk, legyen ez
most mondjuk a Munkakönyvtár nevű munkakönyvtár, majd az egyik ablakban a szervert, a másikban a klienst
fordítsuk és futtassuk!
[norbi@omega Munkakönyvtár]$ javac Szerver.java
[norbi@omega Munkakönyvtár]$ java Szerver
[norbi@omega Munkakönyvtár]$ javac Kliens.java
[norbi@omega Munkakönyvtár]$ java Kliens
Helló, Világ!
[norbi@omega Munkakönyvtár]$ java Kliens
Helló, Világ!
[norbi@omega Munkakönyvtár]$ java Kliens
Helló, Világ!
Page 298
Java mellékletek
270 Created by XMLmind XSL-FO Converter.
Most pedig röviden beszéljük át a forrásokat! Tartsuk közben szem előtt A források olvasásáról című pont
felhívását. Az API dokumentáció telepítésének leírását A Java dokumentáció, azaz az API doksi letöltése című
pontban találjuk meg.
Az internet születésének hajnalán, valamikor az ARPANET korszakban vált de facto szabvánnyá a TCP/IP,
amihez a Berkeley egyetemen kifejlesztettek egy programozói interfészt, ez a socket interfész. A Java is
lehetőséget ad ezen az absztrakciós szinten dolgozni, az ezt lehetővé tevő osztályok a java.net csomagban
kaptak helyet. Az API dokumentációval párhuzamosan olvassuk a program szövegét!
A Szerver osztály indító main() függvényében megpróbáljuk lefoglalni a 2006 számú szerver socketet,
megpróbálunk létrehozni egy ServerSocket, vagyis egy szerver socketet absztraháló Java objektumot a 2006
sorszámú
try {
java.net.ServerSocket serverSocket =
new java.net.ServerSocket(2006);
TCP kapu felett. Ha nem sikerül, akkor egy kivétel keletkezik, s a program végrehajtása egy megfelelő, most a
catch ággal bevezetett kivételkezelő blokkban folytatódik, amivel most nem foglalkozunk.
Tehát ha sikerrel ráültetjük a 2006-os TCP kapura a ServerSocket objektumunkat, akkor programunk egy
végtelen ciklusba kezd, melyben nem tesz mást, mint a szerver socket felett blokkolódva várakozik a kliensek
kapcsolatfelvételi kérelmeinek beérkezésére:
while(true) {
java.net.Socket socket = serverSocket.accept();
Ha egy kliens kapcsolatfelvételi kérelme érkezik a 2006 sorszámú kapuhoz, akkor az eddig blokkolódott
accept() metódus visszaadja a kliens socketet.
Amelytől megpróbálunk elkérni egy kimenő csatorna objektumot, amelyre írni akarjuk a kliensnek küldeni
kívánt adatokat, most a Helló, Világ!\n karaktersorozatot.
java.io.PrintWriter kimenőCsatorna =
new java.io.PrintWriter(socket.getOutputStream());
kimenőCsatorna.println("Helló, Világ!\n");
Majd lezárjuk a kliensre mutató kaput:
socket.close();
A kliens működése egyszerűbb: megpróbál csatlakozni a szerverhez, majd a kapcsolattól egy bejövő csatornát
kérni és egy sort beolvasni.
3. Egyszerű összehasonlítások
3.1. Egyszerű összehasonlítások a sebesség kérdésében
Page 299
Java mellékletek
271 Created by XMLmind XSL-FO Converter.
A sebességek egyszerű összevetésére a PiBBP osztályunkból egyszerűsített PiBBPBench osztályt és annak C
illetve C Sharp nyelvi átíratait használjuk, illetve a Java osztályból a bináris futtathatóval is számolunk. A
következő két táblázatban tehát a a Pi hexadecimális kifejtésének (0. pozíciótól számított) 106., 107., 108. hexa
jegyét határozzuk meg és közben megmérjük az ehhez szükséges időt.
Az első táblázat számításait egy Fedora Linux Core 5 operációs rendszerrel felszerelt, 2.6.17.7 verziójú
kernellel ellátott Sun W1100Z Workstation (AMD Opteron Mhz processzor, 1G memória) gépen gcc (GCC)
4.1.0 20060304 (Red Hat 4.1.0-3) verziójú gcc (gij (GNU libgcj) version 4.1.0 20060304 (Red
Hat 4.1.0-3)) és java version "1.6.0-beta2" verziójú Sun-os Java, a Java Platform, Standard Edition 6
Development Kit mellett végeztük el.
A második táblázat számításait egy Windows XP operációs rendszerrel ellátott Intel Celeron 1.7Ghz
processzoros, 768 M memóriával felszerelt gépen C Sharp tekintetében a Microsoft .NET Framework Software
Development Kit 2.0 és Java tekintetében a java version "1.6.0-beta2" verziójú Sun-os Java, a Java
Platform, Standard Edition 6 Development Kit mellett végeztük el.
A C forrásból készítünk bináris futtathatót, majd a Java forrásból ugyancsak bináris futtathatót, végül a Java
forrásból a hagyományos class bájtkódot. A fordítások után mindhármat futtatjuk egymás után.
[norbi@niobe ~]$ gcc pi_bbp_bench.c -o pi_bbp_bench -lm
[norbi@niobe ~]$ gcj -o pibbpbench --main=PiBBPBench PiBBPBench.java
[norbi@niobe ~]$ javac PiBBPBench.java
[norbi@niobe ~]$ ./pi_bbp_bench
6
4.390000
[norbi@niobe ~]$ ./pibbpbench
6
6.386
[norbi@niobe ~]$ java PiBBPBench
6
4.246
A forrásokban megemeljük a d értékét, most a 107. jegyet vizsgáljuk. Újra fordítunk, majd futtatunk.
[norbi@niobe ~]$ gcc pi_bbp_bench.c -o pi_bbp_bench -lm
[norbi@niobe ~]$ gcj -o pibbpbench --main=PiBBPBench PiBBPBench.java
[norbi@niobe ~]$ javac PiBBPBench.java
[norbi@niobe ~]$ ./pi_bbp_bench
7
51.190000
[norbi@niobe ~]$ ./pibbpbench
7
74.503
[norbi@niobe ~]$ java PiBBPBench
7
49.465
A forrásokban tovább emeljük a d értékét, most a 108. jegyet vizsgáljuk. Megint újra fordítunk, majd futtatunk.
[norbi@niobe ~]$ gcc pi_bbp_bench.c -o pi_bbp_bench -lm
[norbi@niobe ~]$ gcj -o pibbpbench --main=PiBBPBench PiBBPBench.java
[norbi@niobe ~]$ javac PiBBPBench.java
[norbi@niobe ~]$ ./pi_bbp_bench
12
586.000000
[norbi@niobe ~]$ ./pibbpbench
12
854.993
Page 300
Java mellékletek
272 Created by XMLmind XSL-FO Converter.
[norbi@niobe ~]$ java PiBBPBench
12
556.935
Az alábbi táblázatba foglaljuk eredményeinket.
A.1. táblázat - Java, gcj és C egyszerű sebesség összehasonlítása
Pozíció 0xJegy C [sec] gcj [sec] Java [sec]
106 6 4.39 6.386 4.246
107 7 51.19 74.503 49.465
108 C 586.0 854.993 556.935
C:\...> csc PiBBPBench.cs
C:\...> javac PiBBPBench.java
C:\...> PiBBPBench
6
10,65625
C:\...> java PiBBPBench
6
12.578
A forrásokban megemeljük a d értékét, most a 107. jegyet vizsgáljuk. Újra fordítunk, majd futtatunk.
C:\...> csc PiBBPBench.cs
C:\...> javac PiBBPBench.java
C:\...> PiBBPBench
7
125,0625
C:\...> java PiBBPBench
7
147.407
Az alábbi táblázatba foglaljuk eredményeinket.
A.2. táblázat - Java és C# egyszerű sebesség összehasonlítása
Pozíció 0xJegy C# [sec] Java [sec]
106 6 10,65625 12.578
107 7 125,0625 147.407
3.1.1. A PiBBPBench Java osztály
A PiBBPBench.java állomány kódja.
Page 301
Java mellékletek
273 Created by XMLmind XSL-FO Converter.
/*
* PiBBPBench.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* A PiBBP.java-ból kivettük az "objektumorientáltságot", így kaptuk
* ezt az osztályt.
*
* (A PiBBP osztály a BBP (Bailey-Borwein-Plouffe) algoritmust a Pi hexa
* jegyeinek számolását végző osztály. A könnyebb olvahatóság
* kedvéért a változó és metódus neveket megpróbáltuk az algoritmust
* bemutató [BBP ALGORITMUS] David H. Bailey: The BBP Algorithm for Pi.
* cikk jelöléseihez.)
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
*/
public class PiBBPBench {
/**
* BBP algoritmus a Pi-hez, a [BBP ALGORITMUS] David H. Bailey: The
* BBP Algorithm for Pi. alapján a {16^d Sj} részlet kiszámítása.
*
* @param d a d+1. hexa jegytől számoljuk a hexa jegyeket
* @param j Sj indexe
*/
public static double d16Sj(int d, int j) {
double d16Sj = 0.0d;
for(int k=0; k<=d; ++k)
d16Sj += (double)n16modk(d-k, 8*k + j) / (double)(8*k + j);
/* (bekapcsolva a sorozat elejen az első utáni jegyekben növeli pl.
a pontosságot.)
for(int k=d+1; k<=2*d; ++k)
d16Sj += Math.pow(16.0d, d-k) / (double)(8*k + j);
*/
return d16Sj - Math.floor(d16Sj);
}
/**
* Bináris hatványozás mod k, a 16^n mod k kiszámítása.
*
* @param n kitevő
* @param k modulus
*/
public static long n16modk(int n, int k) {
int t = 1;
while(t <= n)
t *= 2;
long r = 1;
while(true) {
if(n >= t) {
r = (16*r) % k;
n = n - t;
}
t = t/2;
if(t < 1)
break;
r = (r*r) % k;
}
Page 302
Java mellékletek
274 Created by XMLmind XSL-FO Converter.
return r;
}
/**
* A [BBP ALGORITMUS] David H. Bailey: The
* BBP Algorithm for Pi. alapján a
* {16^d Pi} = {4*{16^d S1} - 2*{16^d S4} - {16^d S5} - {16^d S6}}
* kiszámítása, a {} a törtrészt jelöli. A Pi hexa kifejtésében a
* d+1. hexa jegytől
*/
public static void main(String args[]) {
double d16Pi = 0.0d;
double d16S1t = 0.0d;
double d16S4t = 0.0d;
double d16S5t = 0.0d;
double d16S6t = 0.0d;
int jegy = 0;
long delta = System.currentTimeMillis();
for(int d=1000000; d<1000001; ++d) {
d16Pi = 0.0d;
d16S1t = d16Sj(d, 1);
d16S4t = d16Sj(d, 4);
d16S5t = d16Sj(d, 5);
d16S6t = d16Sj(d, 6);
d16Pi = 4.0d*d16S1t - 2.0d*d16S4t - d16S5t - d16S6t;
d16Pi = d16Pi - Math.floor(d16Pi);
jegy = (int)Math.floor(16.0d*d16Pi);
}
System.out.println(jegy);
delta = System.currentTimeMillis() - delta;
System.out.println(delta/1000.0);
}
}
3.1.2. A pi_bbp_bench forrás
A pi_bbp_bench.c állomány kódja.
#include <stdio.h>
#include <math.h>
#include <time.h>
/*
* pi_bbp_bench.c
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
* A PiBBP.java-ból kivettük az "objektumorientáltságot", így kaptuk
* a PiBBPBench osztályt, amit pedig átírtuk C nyelvre.
*
*/
/*
* 16^n mod k
* [BBP ALGORITMUS] David H. Bailey: The
Page 303
Java mellékletek
275 Created by XMLmind XSL-FO Converter.
* BBP Algorithm for Pi. alapján.
*/
long
n16modk (int n, int k)
{
long r = 1;
int t = 1;
while (t <= n)
t *= 2;
for (;;)
{
if (n >= t)
{
r = (16 * r) % k;
n = n - t;
}
t = t / 2;
if (t < 1)
break;
r = (r * r) % k;
}
return r;
}
/* {16^d Sj}
* [BBP ALGORITMUS] David H. Bailey: The
* BBP Algorithm for Pi. alapján.
*/
double
d16Sj (int d, int j)
{
double d16Sj = 0.0;
int k;
for (k = 0; k <= d; ++k)
d16Sj += (double) n16modk (d - k, 8 * k + j) / (double) (8 * k + j);
/*
for(k=d+1; k<=2*d; ++k)
d16Sj += pow(16.0, d-k) / (double)(8*k + j);
*/
return d16Sj - floor (d16Sj);
}
/*
* {16^d Pi} = {4*{16^d S1} - 2*{16^d S4} - {16^d S5} - {16^d S6}}
* [BBP ALGORITMUS] David H. Bailey: The
* BBP Algorithm for Pi. alapján.
*/
main ()
{
double d16Pi = 0.0;
double d16S1t = 0.0;
double d16S4t = 0.0;
double d16S5t = 0.0;
double d16S6t = 0.0;
int jegy;
int d;
Page 304
Java mellékletek
276 Created by XMLmind XSL-FO Converter.
clock_t delta = clock ();
for (d = 1000000; d < 1000001; ++d)
{
d16Pi = 0.0;
d16S1t = d16Sj (d, 1);
d16S4t = d16Sj (d, 4);
d16S5t = d16Sj (d, 5);
d16S6t = d16Sj (d, 6);
d16Pi = 4.0 * d16S1t - 2.0 * d16S4t - d16S5t - d16S6t;
d16Pi = d16Pi - floor (d16Pi);
jegy = (int) floor (16.0 * d16Pi);
}
printf ("%d\n", jegy);
delta = clock () - delta;
printf ("%f\n", (double) delta / CLOCKS_PER_SEC);
}
3.1.3. A PiBBPBench C Sharp osztály
A PiBBPBench.cs állomány kódja.
/*
* FileName: PiBBPBench.cs
* Author: Bátfai Norbert, [email protected]
* DIGIT 2005, Javat tanítok
*/
/// <summary>
/// A PiBBPBench C# átírata.
/// </summary>
/// <remark>
/// A PiBBP.java-ból kivettük az "objektumorientáltságot", így kaptuk
/// a PiBBPBench osztályt, amit pedig átírtuk C# nyelvre.
///
/// (A PiBBP osztály a BBP (Bailey-Borwein-Plouffe) algoritmust a Pi hexa
/// jegyeinek számolását végző osztály. A könnyebb olvahatóság
/// kedvéért a változó és metódus neveket megpróbáltuk az algoritmust
/// bemutató [BBP ALGORITMUS] David H. Bailey: The BBP Algorithm for Pi.
/// cikk jelöléseihez.)
/// </remark>
public class PiBBPBench {
/// <remark>
/// BBP algoritmus a Pi-hez, a [BBP ALGORITMUS] David H. Bailey: The
/// BBP Algorithm for Pi. alapján a {16^d Sj} részlet kiszámítása.
/// </remark>
/// <param>
/// d a d+1. hexa jegytől számoljuk a hexa jegyeket
/// </param>
/// <param>
/// j Sj indexe
/// </param>
public static double d16Sj(int d, int j) {
double d16Sj = 0.0d;
for(int k=0; k<=d; ++k)
d16Sj += (double)n16modk(d-k, 8*k + j) / (double)(8*k + j);
/*
for(int k=d+1; k<=2*d; ++k)
Page 305
Java mellékletek
277 Created by XMLmind XSL-FO Converter.
d16Sj += System.Math.pow(16.0d, d-k) / (double)(8*k + j);
*/
return d16Sj - System.Math.Floor(d16Sj);
}
/// <summary>
/// Bináris hatványozás mod k, a 16^n mod k kiszámítása.
/// </summary>
/// <param>
/// n kitevő
/// </param>
/// <param>
/// k modulus
/// </param>
public static long n16modk(int n, int k) {
int t = 1;
while(t <= n)
t *= 2;
long r = 1;
while(true) {
if(n >= t) {
r = (16*r) % k;
n = n - t;
}
t = t/2;
if(t < 1)
break;
r = (r*r) % k;
}
return r;
}
/// <remark>
/// A [BBP ALGORITMUS] David H. Bailey: The
/// BBP Algorithm for Pi. alapján a
/// {16^d Pi} = {4*{16^d S1} - 2*{16^d S4} - {16^d S5} - {16^d S6}}
/// kiszámítása, a {} a törtrészt jelöli. A Pi hexa kifejtésében a
/// d+1. hexa jegytől
/// </remark>
public static void Main(System.String[]args) {
double d16Pi = 0.0d;
double d16S1t = 0.0d;
double d16S4t = 0.0d;
double d16S5t = 0.0d;
double d16S6t = 0.0d;
int jegy = 0;
System.DateTime kezd = System.DateTime.Now;
for(int d=1000000; d<1000001; ++d) {
d16Pi = 0.0d;
d16S1t = d16Sj(d, 1);
d16S4t = d16Sj(d, 4);
d16S5t = d16Sj(d, 5);
d16S6t = d16Sj(d, 6);
d16Pi = 4.0d*d16S1t - 2.0d*d16S4t - d16S5t - d16S6t;
d16Pi = d16Pi - System.Math.Floor(d16Pi);
Page 306
Java mellékletek
278 Created by XMLmind XSL-FO Converter.
jegy = (int)System.Math.Floor(16.0d*d16Pi);
}
System.Console.WriteLine(jegy);
System.TimeSpan delta = System.DateTime.Now.Subtract(kezd);
System.Console.WriteLine(delta.TotalMilliseconds/1000.0);
}
}
3.2. A Java és a C Sharp nyelvi szintű összehasonlítása
Az elmaradhatatlan, alább bemutatásra kerülő „Helló, világ!” programok jól mutatják, hogy érdekesebb és
érdemesebb az összehasonlítást a két nyelv közötti hasonlóságokkal kezdeni.
public class HellóVilág {
public static void main(String args[]) {
System.out.println("Helló Világ!");
}
}
A HellóVilág osztály statikus, azaz nem az osztályból származtatott példányokhoz, hanem magához az
osztályhoz tartozó indító main függvényének egyetlen utasítása arról szól, hogy a meghívjuk a System osztály
out tagjának println() metódusát, aminek paraméteréül a "Helló Világ!" sztringet adjuk. A megszólított
out tag a System osztály egy PrintStream osztálybeli (típusú) változója, s mint ilyen egy kimeneti csatorna; a
programhoz rendelt sztenderd kimeneti csatorna: tipikusan a képernyő. Tehát a programot a javac nevű Java
fordítóprogrammal lefordítva és futtatva, azaz az osztályt a javac Java Virtuális Gépnek átadva, a kimenet a
parancsablakban jelenik meg:
C:\...> javac HellóVilág.java
C:\...> java HellóVilág
Helló Világ!
A Java számtalan osztályt biztosít a fejlesztő rendelkezésére, amiket az egyszerűbb kezelés érdekében egy fa
szerkezetbe szervez. Ezt a fa szerkezetet nevezzük Java API-nak. A System osztály például a java alól nyíló
lang ágban van, amit a Java terminológia egyébként csomagnak nevez és röviden, ponttal minősítve a
java.lang alakban ír. Tehát a System osztály teljes neve java.lang.System, de ez a csomag annyira
általános, hogy eltekinthetünk és szokás szerint el is tekintünk a kiírásától.
Ránézésre a megfelelő C Sharp programunk megegyezik a Java változattal, de itt a System nem osztály, hanem
egy névtér. Ami jelen összehasonlításunkban a java.lang csomaggal állítható rokonságba. A Console viszont
már egy osztály, a konzolt absztraháló osztály, minek WriteLine() metódusával tudunk a sztenderd kimenetre
írni. (Ebben az értelemben a System.Console.WriteLine("Helló Világ!"); utasításnak Javaban a
java.lang.System.out.println("Helló Világ!"); utasítás felel meg formálisan.)
public class HellóVilág {
public static void Main() {
System.Console.WriteLine("Helló Világ!");
Page 307
Java mellékletek
279 Created by XMLmind XSL-FO Converter.
}
}
A programot a csc nevű C Sharp fordítóprogrammal [.NET Framework SDK 2.0] lefordítva és futtatva a
kimenet az ablakban jelenik meg:
C:\...> csc HellóVilág.cs
C:\...> HellóVilág.exe
Helló Világ!
A következő példával nagyot ugrunk, a bemutatásra kerülő többszálú szerveroldali TCP kiszolgáló programmal
továbbra is az a célunk, hogy kihangsúlyozzuk a nyelvek közötti hasonlóságokat.
Figyelmeztetés a Javaban kezdő Olvasóknak
A következő Java és C Sharp nyelvű hálózati példát csak felületesen fussa át a kezdő Olvasó. De most
akár ki is hagyhatja, s az olvasást a következő, az alapvető programnyelvi konstrukciókat bemutató
résznél folytathatja. Ez esetben a hálózati alapfogalmakat és a Java programozást bevezető tárgyaló
rész után érdemes ide visszalapozni. A részletesebb tárgyalás előtt vessünk egy pillantást a Java megvalósításunk alábbi fő osztályára!
public class Szerver {
public static void main(String [] args) {
try {
java.net.ServerSocket serverSocket =
new java.net.ServerSocket(2006);
while(true) {
java.net.Socket socket = serverSocket.accept();
new Thread(new Kiszolgáló(socket)).start();
}
} catch(java.io.IOException ioE) {
ioE.printStackTrace();
}
}
}
A Szerver osztály indító függvényében megpróbáljuk lefoglalni a 2006 számú szerver socketet, azaz OO
nyelven: megpróbálunk létrehozni egy ServerSocket, egy szerver socketet absztraháló Java objektumot a 2006
sorszámú
try {
java.net.ServerSocket serverSocket =
new java.net.ServerSocket(2006);
TCP kapu felett. Ha nem sikerül, akkor egy kivétel, OO terminológiában kivétel objektum keletkezik, s a
program végrehajtása egy megfelelő kivételkezelő keresésével folytatódik. Ez a kivétel objektum tipikusan egy
Page 308
Java mellékletek
280 Created by XMLmind XSL-FO Converter.
BindException osztálybeli objektum szokott lenni, ami azt jelöli, hogy a lefoglalni óhajtott 2006 sorszámú
kapu éppen foglalt. Ez a kivétel osztály leszármazottja az IOException osztálynak, így a kivétel objektum
kezelésére a
} catch(java.io.IOException ioE) {
ioE.printStackTrace();
}
kivételkezelő blokk alkalmas, ahol egyébként nem teszünk mást, mint kiírjuk, hogy hogyan került ebbe a
helyzetbe a programunk. Majd programunk a kivételkezelő blokk utáni résszel folytatódik, azaz gyakorlatilag
véget ér.
Ha viszont sikerrel járunk, akkor programunk egy végtelen ciklusba kezd, melyben nem tesz mást, mint a
szerver socket felett blokkolódva várakozik a kliensek kapcsolatfelvételi kérelmeinek beérkezésére:
while(true) {
java.net.Socket socket = serverSocket.accept();
ha egy kliens kapcsolatfelvételi kérelme érkezik a 2006 sorszámú kapuhoz, akkor az eddig blokkolódott
accept() metódus visszaadja a kliens socketet. A jelentkező kliens kiszolgálására külön szálat, azaz
programunk eddig tekintett fő szálával párhuzamosan futó külön szálat készítünk. Mivel minden, így a
párhuzamosan futó szálak is objektumok, ezek a Thread osztálybeli objektumok. A példányosításkor a szálnak
átadjuk a párhuzamosan végrehajtani kívánt kódot implementáló objektumot, egy a kliens socket fölött éppen
létrehozott Kiszolgáló objektumot.
new Thread(new Kiszolgáló(socket)).start();
A létrehozott szál objektumot start() metódusával rögtön el is indítjuk, miután programunk végrehajtása ketté
vált:
• a program fő szálában a vezérlés a végtelen ciklusban folytatódik tovább, azaz program újra várakozni kezd
az accept() metódusban.
• az indított szálban a párhuzamosan végrehajtani kívánt kódot implementáló objektum run() metódusának
végrehajtása kezdődik meg.
De mielőtt a Kiszolgáló objektumot és annak run() metódusát megvizsgálnánk, nézzük meg az eddig
bemutatott szerverünk C Sharp nyelvi megvalósítását!
public class Szerver {
public static void Main(System.String[]args) {
try {
System.Net.Sockets.TcpListener tcpListener =
new System.Net.Sockets.TcpListener(
System.Net.IPAddress.Loopback, 2006);
tcpListener.Start();
while (true) {
Page 309
Java mellékletek
281 Created by XMLmind XSL-FO Converter.
System.Net.Sockets.TcpClient socket =
tcpListener.AcceptTcpClient();
Kiszolgáló kiszolgáló = new Kiszolgáló(socket);
System.Threading.ThreadStart threadStart =
new System.Threading.ThreadStart(kiszolgáló.run);
System.Threading.Thread szál =
new System.Threading.Thread(threadStart);
szál.Start();
}
} catch (System.Exception e) {
System.Console.WriteLine(e);
}
}
}
Jól láthatóan a C Sharp megvalósítás dallama ugyanaz, mint a Java megvalósításé volt.
Folytassuk az iménti C Sharp kitérő miatt felfüggesztett Java nyelven implementált Kiszolgáló osztály
tárgyalását. A részletek előtt megint csak vessünk egy pillantást az egész osztályra!
class Kiszolgáló implements Runnable {
java.net.Socket socket;
public Kiszolgáló(java.net.Socket socket) {
this.socket = socket;
}
public void run() {
try {
java.io.BufferedReader bejövőCsatorna =
new java.io.BufferedReader(
new java.io.InputStreamReader(socket.getInputStream()));
java.io.PrintWriter kimenőCsatorna =
new java.io.PrintWriter(socket.getOutputStream());
String sor = null;
while((sor = bejövőCsatorna.readLine()) != null) {
kimenőCsatorna.println(sor);
kimenőCsatorna.flush();
}
socket.close();
} catch(java.io.IOException ioE) {
ioE.printStackTrace();
}
}
}
Az osztály a Runnable interfész implementálásával jelzi, hogy tartalmaz egy párhuzamosan végrehajtható
run() metódust.
class Kiszolgáló implements Runnable {
Page 310
Java mellékletek
282 Created by XMLmind XSL-FO Converter.
A run() metódusban megpróbálunk két szöveges csatorna objektumot készíteni a kliens kapu fölött, egy
bemenetit, amelyről olvasni szeretnénk a klienstől érkező adatokat és egy kimenetit, amelyre írni akarjuk a
kliensnek küldeni kívánt adatokat.
try {
java.io.BufferedReader bejövőCsatorna =
new java.io.BufferedReader(
new java.io.InputStreamReader(socket.getInputStream()));
java.io.PrintWriter kimenőCsatorna =
new java.io.PrintWriter(socket.getOutputStream());
A csatorna objektumok sikeres elkészítése után a bejövő csatornáról a BufferedReader osztály readLine()
módszerével sorokat olvasunk be, egészen addig, ameddig csak tudunk. Ha már nem tudunk, akkor ezt a
readLine() metódus majd a null érték visszaadásával jelzi.
while((sor = bejövőCsatorna.readLine()) != null) {
kimenőCsatorna.println(sor);
kimenőCsatorna.flush();
}
A bejövő csatornáról olvasó ciklus testében nem teszünk mást, mint a beolvasott sztring visszaírását a kimenő
csatornára. Tehát szerverünk egy echo szerver jelleggel működik. Ha már nincs több olvasható sor a bejövő
csatornán, akkor kilépünk az Olvasó ciklusból és lezárjuk a kliensre mutató kaput.
socket.close();
Nézzük meg az imént Java nyelven implementált Kiszolgáló osztály C Sharp nyelven készített megvalósítását!
class Kiszolgáló {
System.Net.Sockets.TcpClient socket;
public Kiszolgáló(System.Net.Sockets.TcpClient socket) {
this.socket = socket;
}
public void run() {
System.IO.StreamReader bejövőCsatorna =
new System.IO.StreamReader(socket.GetStream());
System.IO.StreamWriter kimenőCsatorna =
new System.IO.StreamWriter(socket.GetStream());
System.String sor;
while((sor = bejövőCsatorna.ReadLine()) != null) {
kimenőCsatorna.WriteLine(sor);
Page 311
Java mellékletek
283 Created by XMLmind XSL-FO Converter.
kimenőCsatorna.Flush();
}
socket.Close();
}
}
Ismét jól látható, hogy a C Sharp megvalósítás dallama ugyanaz, mint ami a Java megvalósításé volt.
3.2.1. Az alapvető nyelvi elemek összehasonlítása
Ebben a pontban - csupán demonstrációs céllal - néhány nyelvi elem leírására vetünk egy-egy pillantást.
Alapvetően a Java nyelvi konstrukciókat tárgyaljuk, de ha külön nem emeljük ki a C Sharp nyelvbeli
különbséget, akkor az megegyezik a Java konstrukcióval.
Osztályt a
public class OsztályNév {
}
alakban definiálunk. Javaban fontos, hogy ezt a publikus OsztályNév osztály definíciót tartalmazó szöveges
forrásállomány neve megegyezzen ezzel a publikus osztálynévvel, azaz az állomány neve OsztályNév.java
legyen.
Nem javasoljuk, de egyetlen forrásállomány több, nem publikus osztály definícióját is tartalmazhatja.
public class OsztályNév {
}
class MásikOsztályNév {
}
Ezek az osztálydefiníciók (akár a most tárgyalt üresek is) lefordíthatók a javac nevű Java fordítóprogrammal.
C:\...> javac OsztályNév.java
C Sharpban az osztály lefordításához egy belépési pontra is szükség van, ez a C örökségnek megfelelően a
main() függvény.
public class OsztályNév {
public static void Main() {
}
}
public class MásikOsztályNév {
Page 312
Java mellékletek
284 Created by XMLmind XSL-FO Converter.
}
Javaban ugyancsak, de itt az esetleges parancssor argumentumok átvételét szolgáló String[] args sztring
tömböt is szerepeltetnünk kell a belépési függvény formális paraméter listájában.
public class OsztályNév {
public static void main(String[] args) {
}
}
class MásikOsztályNév {
}
De ugyanezzel a konstrukcióval vesszük át a parancssor argumentumokat C Sharpban is:
public static void Main(System.String[] args) {
}
A main() függvényben rendszerint példányosítunk, azaz a new operátorral objektumot, objektumokat hozunk
létre.
public static void main(String[] args) {
OsztályNév példányReferencia = new OsztályNév("Hello");
}
Itt az OsztályNév osztályból példányosítottunk, azaz létrehoztunk egy konkrét, a továbbiakban a
példányReferencia névvel azonosított objektumot. Ez azt jelenti, hogy lefutott a OsztályNév osztály
megfelelő konstruktora:
public class OsztályNév {
String példányTagReferencia;
public OsztályNév(String példányTagReferencia) {
this.példányTagReferencia = példányTagReferencia;
}
ami nem csinált mást, mint a new OsztályNév("Hello"); hívásban érkező aktuális "Hello" paraméter sztring
objektum referenciáját értékül adja az osztály aktuális példánya példányTagReferencia nevű változójának.
A referenciák neve után egy pontot írva tudjuk megszólítani az objektum példányokat, tagjaikra vagy
módszereikre hivatkozhatunk. Előbbire lássuk az alábbi példát, utóbbira majd a következő feladatot!
Page 313
Java mellékletek
285 Created by XMLmind XSL-FO Converter.
public class OsztályNév {
String példányTagReferencia;
public OsztályNév(String példányTagReferencia) {
this.példányTagReferencia = példányTagReferencia;
}
public static void main(String[] args) {
OsztályNév osztályPéldány = new OsztályNév("Hello");
System.out.println(osztályPéldány.példányTagReferencia);
}
}
A.1. példa - Írjunk az OsztályNév osztályunkhoz egy példánymetódust, ami visszaadja az
osztály String tagját!
public class OsztályNév {
String példányTagReferencia;
public OsztályNév(String példányTagReferencia) {
this.példányTagReferencia = példányTagReferencia;
}
public String példányMetódus() {
return példányTagReferencia;
}
public static void main(String[] args) {
OsztályNév osztályPéldány = new OsztályNév("Hello");
System.out.println(osztályPéldány.példányTagReferencia);
System.out.println(osztályPéldány.példányMetódus());
}
}
Ha a kedves Olvasó lefordítaná és futtatná a példát, akkor az alábbi eredményeket kapná.
C:\...> javac OsztályNév.java
C:\...> java OsztályNév
Hello
Hello
Adjuk meg a megoldást C Sharp nyelven is:
Page 314
Java mellékletek
286 Created by XMLmind XSL-FO Converter.
public class OsztályNév {
System.String példányTagReferencia;
public OsztályNév(System.String példányTagReferencia) {
this.példányTagReferencia = példányTagReferencia;
}
public System.String példányMetódus() {
return példányTagReferencia;
}
public static void Main() {
OsztályNév osztályPéldány = new OsztályNév("Hello");
System.Console.WriteLine(osztályPéldány.példányTagReferencia);
System.Console.WriteLine(osztályPéldány.példányMetódus());
}
}
A Java példához hasonlóan, ha a kedves Olvasó lefordítaná és futtatná a példát, akkor az alábbi eredményeket
kapná.
C:\...> csc Osztály.java
C:\...> Osztály.exe
Hello
Hello
A Java és a C Sharp összehasonlítása iránt tovább érdeklődő kedves Olvasónak a [JAVA ás C SHARP], [C
SHARP J--] cikkeket ajánljuk.
Page 315
287 Created by XMLmind XSL-FO Converter.
B. függelék - Számítási mellékletek
„Mégis kitartok a remény mellett, hogy az értelem megértésében a természettudományokon és a matematikán
keresztül kell komoly előrehaladásnak létrejönnie.” —Roger Penrose A császár új elméje
1. Biológiai témájú programok
1.1. Genomi, aminosav vagy akár tetszőleges szekvenciák összehasonlítása
Ebben a pontban egy olyan (Swinges) grafikus felülettel ellátott pontmátrixot kiszámoló/megmutató/kimentő
programot készítünk, amivel tetszőleges sorozatokat, de főleg genomi vagy fehérje szekvenciákat tudunk
összehasonlítani. A pontmátrix részletes leírását a [BIOINFOMATIKA] könyvben találjuk. A pontmátrix
egyszerűen a mátrix, ami i. oszlopának és j. sorának értéke fekete, azaz az alábbi kódban 0x00000000, ha az
egyik minta i. eleme és a másik minta j. eleme megegyezik, különben 0x00ffffff fehér.
// A pontmátrix számítása:
for(int i=0; i<egyikMinta.length; ++i) {
for(int j=0; j<másikMinta.length; ++j) {
pontmátrixKép.setRGB(i,j, 0x00ffffff);
if(egyikMinta[i] == másikMinta[j])
pontmátrixKép.setRGB(i,j, 0x00000000);
}
}
A pontmátrix arról ad tehát tájékoztatást, hogy a két vizsgált minta mennyire egyezik meg. Ha például mindkét
mintának ugyanazt a sorozatot adjuk meg, akkor egy átlós egyenes jól láthatóvá válik a pontmátrixon.
A fejlesztendő pontmátrixos programot legördülő menüvel készítjük el, e menün keresztül vesszük át a
felhasználói inputot: a minták betöltését, a pontmátrix számításának indítását stb.
Page 316
Számítási mellékletek
288 Created by XMLmind XSL-FO Converter.
A minta szekvenciákat tartalmazó állományok betöltését és a kiszámolt pontmátrix képek mentését a
javax.swing.JFileChooser osztály segítségével végezzük. Az alábbi ábra egy ilyen objektum üzemét
mutatja az egyik minta betöltésénél.
A javax.swing.JFileChooser osztály használata kényelmes és egyszerű, például a kimentést megvalósító
kódunk az alábbi lesz.
} else if("Mentés...".equals(menü)) {
javax.swing.JFileChooser betöltő = new javax.swing.JFileChooser();
betöltő.showSaveDialog(getContentPane());
Page 317
Számítási mellékletek
289 Created by XMLmind XSL-FO Converter.
pillanatfelvétel(betöltő.getSelectedFile().getAbsolutePath());
}
ahol a betöltő.getSelectedFile().getAbsolutePath() a
ablakban megadott Ember_egér_tubulin_összehasonlítása.png állománynevet adja vissza, a
pillanatfelvétel() függvény pedig ebbe a paraméterként kapott nevű állományba menti majd a pontmátrix
képét.
A következő ábra üzem közben mutatja a példaprogramunkat. Éppen az
• ember http://www.expasy.org/uniprot/Q13748 (UniProtKB-Swiss-Prot entry Q13748 [TBA2_HUMAN]
Tubulin alpha-2 chain)
• és az egér http://www.expasy.org/uniprot/P05213 (UniProtKB-Swiss-Prot entry P05213 [TBA2_MOUSE]
Tubulin alpha-2 chain)
alfa tubulin 2 fehérjéjének kódját hasonlítjuk össze.
Page 318
Számítási mellékletek
290 Created by XMLmind XSL-FO Converter.
A kapott kép láthatóan zajos, ezért implementáltuk a Pontmátrix/Szűrés menüpontot is, amely annyiban több az
egyszerű számításnál, hogy az egyezéseket nem csupán a vizsgált szekvenciák egyedi tagjainál, hanem kis
csoportjaiban vizsgáljuk. Azaz nem csupán az i. és j. elemnek kell megegyeznie, hanem környezetükből többnek
is.
Page 319
Számítási mellékletek
291 Created by XMLmind XSL-FO Converter.
// A pontmátrix szűrése:
int ablakMéret = 7;
for(int i=0; i<egyikMinta.length; ++i) {
for(int j=0; j<másikMinta.length; ++j) {
int egyforma = 0;
for(int k = -ablakMéret; k<ablakMéret; ++k) {
if( i+k > 0 && i+k < egyikMinta.length
&& j+k > 0 && j+k < másikMinta.length)
if(egyikMinta[i+k] == másikMinta[j+k])
++egyforma;
}
pontmátrixKép.setRGB(i,j, 0x00ffffff);
if(egyforma > 2*ablakMéret-2)
pontmátrixKép.setRGB(i,j, 0x00000000);
}
}
A programmal végzett számítás (és szűrés) jól mutatja,
• az ember http://www.expasy.org/uniprot/Q13748
• és egér http://www.expasy.org/uniprot/P05213
• és az ember és C. elegans fonalféreg a http://www.expasy.org/uniprot/P34690
alfa tubulin 2 fehérjéjének hasonlóságát:
Page 320
Számítási mellékletek
292 Created by XMLmind XSL-FO Converter.
Láthatóan az emberé és az egéré sokkal inkább hasonló, mint alább, az emberé és a fonalféregé.
1.1.1. A Pontmátrix osztály
/*
* Pontmátrix.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
Page 321
Számítási mellékletek
293 Created by XMLmind XSL-FO Converter.
/**
* A pontmátrixot tartalmazó képet kirajzoló osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
*/
class PontmátrixVászon extends javax.swing.JPanel {
/** A pontmátrixot tartalmazó kép. */
java.awt.image.BufferedImage pontmátrixKép;
// A panel mérete
java.awt.Dimension mátrixMéret =
new java.awt.Dimension(800, 800);
// A panel mérete
public java.awt.Dimension getPreferredSize() {
return mátrixMéret;
}
/** A pontmátrixot tartalmazó kép kirajzolása. */
public void paintComponent(java.awt.Graphics g) {
super.paintComponent(g);
if(pontmátrixKép != null)
g.drawImage(pontmátrixKép, 0, 0, this);
}
/** A pontmátrixot tartalmazó kép beállítása. */
public void beállítKép(java.awt.image.BufferedImage pontmátrixKép) {
this.pontmátrixKép = pontmátrixKép;
mátrixMéret.setSize(pontmátrixKép.getWidth(),
pontmátrixKép.getHeight());
}
}
/**
* A pontmátrixot számoló osztály.
* A pontmátrix leírását lásd a [BIOINFORMATIKA] hivatkozásban:
* (Maróti Péter: Információelmélet a biológiában, JATEPress 2003.)
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
*/
public class Pontmátrix extends javax.swing.JFrame
implements java.awt.event.ActionListener {
/** Az egyik minta. */
byte [] egyikMinta;
/** A másik minta. */
byte [] másikMinta;
/** A pontmátrix képe. */
java.awt.image.BufferedImage pontmátrixKép;
/** A pontmátrix képét tartalmazó panel. */
PontmátrixVászon pontmátrixVászon;
/** A pontmátrix képét tartalmazó panelt tartalmazó görgető objektum. */
javax.swing.JScrollPane pontmátrixGörgető;
/** Informáló szövegdoboz. */
javax.swing.JTextArea infóTextArea;
/** A GUI felépítése. */
public Pontmátrix() {
// Az ablak adatai, fejléce:
super("Mindenféle szekvenciák összehasonlítása.");
// Pozíció és méret
java.awt.Dimension képernyőMéret =
java.awt.Toolkit.getDefaultToolkit().getScreenSize();
// Középre tesszük:
setBounds((int)képernyőMéret.getWidth()/2-300,
(int)képernyőMéret.getHeight()/2-200,
600, 400);
// Az ablak komponenseinek elhelyezési stratégiája:
// középen lesz a kép (CENTER), alatta (SOUTH) az
// informáló szövegdoboz.
getContentPane().setLayout(new java.awt.BorderLayout());
// Az ablak szokásos bezár gombjára is kilép a program:
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
// Legördülő menü elkészítése.
Page 322
Számítási mellékletek
294 Created by XMLmind XSL-FO Converter.
// A Menüsor:
javax.swing.JMenuBar menüSor = new javax.swing.JMenuBar();
// A Fájl menü
javax.swing.JMenu menü = new javax.swing.JMenu("Fájl");
menüSor.add(menü);
javax.swing.JMenuItem menüPont
= new javax.swing.JMenuItem("Egyik minta...");
menüPont.addActionListener(this);
menü.add(menüPont);
menüPont = new javax.swing.JMenuItem("Másik minta...");
menüPont.addActionListener(this);
menü.add(menüPont);
menüPont = new javax.swing.JMenuItem("Mentés...");
menüPont.addActionListener(this);
menü.add(menüPont);
menüPont = new javax.swing.JMenuItem("Kilépés");
menüPont.addActionListener(this);
menü.add(menüPont);
// A Pontmárix menü
menü = new javax.swing.JMenu("Pontmátrix");
menüPont.addActionListener(this);
menüSor.add(menü);
menüPont = new javax.swing.JMenuItem("Számol");
menüPont.addActionListener(this);
menü.add(menüPont);
menüPont = new javax.swing.JMenuItem("Szűrés");
menüPont.addActionListener(this);
menü.add(menüPont);
// A névjegy menü
menü = new javax.swing.JMenu("Névjegy");
menüPont.addActionListener(this);
menüSor.add(menü);
menüPont = new javax.swing.JMenuItem("Névjegy");
menüPont.addActionListener(this);
menü.add(menüPont);
// A menü hozzáadása az ablakhoz
setJMenuBar(menüSor);
// A pontmátrix képet erre a vászonra (panelre) rajzoljuk majd
pontmátrixVászon = new PontmátrixVászon();
pontmátrixVászon.setSize(800, 800);
// legyen görgethető
pontmátrixGörgető = new javax.swing.JScrollPane(pontmátrixVászon);
getContentPane().add(pontmátrixGörgető,
java.awt.BorderLayout.CENTER);
// Az informáló szövegdoboz
infóTextArea = new javax.swing.JTextArea();
// ez is legyen görgethető
javax.swing.JScrollPane infóScrollPane
= new javax.swing.JScrollPane();
infóScrollPane.setViewportView(infóTextArea);
infóScrollPane.setPreferredSize(new java.awt.Dimension(400, 100));
getContentPane().add(infóScrollPane, java.awt.BorderLayout.SOUTH);
// Kezdeti informáló szöveg
infóTextArea.append("Ez a Javat tanítok kézikönyv pontmátrix " +
"példaprogramja\nA pontmátrix leírását lásd a " +
"[BIOINFORMATIKA] hivatkozásban\n\n" +
"A Fájl/Egyik minta... Másik minta... " +
"menüpontok kiválasztásával tölts be a két mintát,\n" +
"majd alkalmazhatod a Pontmátrix/Számol menüpontot.\n");
// Lássuk!
setVisible(true);
}
/** Eseménykezelés. */
public void actionPerformed(java.awt.event.ActionEvent e) {
String menü = e.getActionCommand();
if("Egyik minta...".equals(menü)) {
egyikMinta = betöltMinta();
} else if("Másik minta...".equals(menü)) {
Page 323
Számítási mellékletek
295 Created by XMLmind XSL-FO Converter.
másikMinta = betöltMinta();
} else if("Számol".equals(menü)) {
if(egyikMinta == null) {
infóTextArea.append("A egyik minta nincs betöltve.\n");
return;
}
if(másikMinta == null) {
infóTextArea.append("A másik minta nincs betöltve.\n");
return;
}
pontmátrixKép =
new java.awt.image.BufferedImage(egyikMinta.length,
másikMinta.length,
java.awt.image.BufferedImage.TYPE_INT_RGB);
// A pontmátrix számítása:
for(int i=0; i<egyikMinta.length; ++i) {
for(int j=0; j<másikMinta.length; ++j) {
pontmátrixKép.setRGB(i,j, 0x00ffffff);
if(egyikMinta[i] == másikMinta[j])
pontmátrixKép.setRGB(i,j, 0x00000000);
}
}
infóTextArea.append("Pontrátrix számítása kész.\n");
// A kiszámolt pontmátrix megjelenítése
pontmátrixVászon.beállítKép(pontmátrixKép);
pontmátrixGörgető.setViewportView(pontmátrixVászon);
} else if("Névjegy".equals(menü)) {
// Egy informáló dialógus ablakot nyitunk a névjegynek:
javax.swing.JOptionPane.showMessageDialog(null,
"Pontmátrix, 0.0.1 verzió\nJavat tanítok példaprogram",
"Névjegy", javax.swing.JOptionPane.INFORMATION_MESSAGE);
} else if("Szűrés".equals(menü)) {
if(pontmátrixKép == null) {
infóTextArea.append("A pontmátrix nincs kiszámítva.\n");
return;
}
// A pontmátrix szűrése:
int ablakMéret = 7;
for(int i=0; i<egyikMinta.length; ++i) {
for(int j=0; j<másikMinta.length; ++j) {
int egyforma = 0;
for(int k = -ablakMéret; k<ablakMéret; ++k) {
if( i+k > 0 && i+k < egyikMinta.length
&& j+k > 0 && j+k < másikMinta.length)
if(egyikMinta[i+k] == másikMinta[j+k])
++egyforma;
}
pontmátrixKép.setRGB(i,j, 0x00ffffff);
if(egyforma > 2*ablakMéret-2)
pontmátrixKép.setRGB(i,j, 0x00000000);
}
}
infóTextArea.append("Pontrátrix szűrése kész.\n");
// A szűrt pontmátrix megjelenítése
pontmátrixVászon.beállítKép(pontmátrixKép);
pontmátrixGörgető.setViewportView(pontmátrixVászon);
} else if("Mentés...".equals(menü)) {
javax.swing.JFileChooser betöltő = new javax.swing.JFileChooser();
betöltő.showSaveDialog(getContentPane());
pillanatfelvétel(betöltő.getSelectedFile().getAbsolutePath());
Page 324
Számítási mellékletek
296 Created by XMLmind XSL-FO Converter.
} else if("Kilépés".equals(menü)) {
System.exit(0);
}
}
/**
* Fájl betöltése fájl kiválasztó ablakkal.
*
* @return byte [] a fájl tartalma, mint minta.
*/
public byte [] betöltMinta() {
javax.swing.JFileChooser betöltő = new javax.swing.JFileChooser();
betöltő.showOpenDialog(getContentPane());
java.io.File fájl = betöltő.getSelectedFile().getAbsoluteFile();
byte [] buffer = new byte[(int)fájl.length()];
try {
java.io.FileInputStream fájlCsatorna =
new java.io.FileInputStream(fájl);
fájlCsatorna.read(buffer, 0, buffer.length);
} catch(java.io.IOException e) {
e.printStackTrace();
}
infóTextArea.append("A " + fájl.getName() + " minta betöltve.\n");
return buffer;
}
/** Pillanatfelvételek készítése. */
public void pillanatfelvétel(String fájlNév) {
// png formátumú képet mentünk
try {
javax.imageio.ImageIO.write(pontmátrixKép, "png",
new java.io.File(fájlNév));
} catch(java.io.IOException e) {
e.printStackTrace();
}
}
/** Példányosít egy Pontmátrix obektumot.*/
public static void main(String [] args) {
new Pontmátrix();
}
}
B.1. példa - Pontmátrix osztály kiegészítése
A pontmátrix programunk névjegye mintájára dialógusablakokban is közöljük a program hibaüzeneteit a
felhasználó felé. Ezt a módszert alkalmaztuk a névjegy információk megjelenítésénél:
} else if("Névjegy".equals(menü)) {
// Egy informáló dialógus ablakot nyitunk a névjegynek:
javax.swing.JOptionPane.showMessageDialog(null,
"Pontmátrix, 0.0.1 verzió\nJavat tanítok példaprogram",
"Névjegy", javax.swing.JOptionPane.INFORMATION_MESSAGE);
Page 325
Számítási mellékletek
297 Created by XMLmind XSL-FO Converter.
Ha például valamelyik minta még nincs betöltve, akkor ezt ne csak az
if(egyikMinta == null) {
infóTextArea.append("A egyik minta nincs betöltve.\n");
return;
}
informáló ablakban, hanem egy dialógus ablakban is közöljük.
javax.swing.JOptionPane.showMessageDialog(null,
"A egyik minta nincs betöltve.",
"Hiba", javax.swing.JOptionPane.ERROR_MESSAGE);
B.2. példa - További kísérletek a Pontmátrix osztályunkkal
A Pi jegyeinek nyomában pont programját felhasználva legenerálhatjuk a Pi hexadecimális kifejtésének
mondjuk első és második ezer hexa jegyét, majd ezeket a pontmátrix módszerrel összehasonlíthatjuk. De
mindkét mintának az első ezer jegyet átadva afelől tudakolódhatunk, hogy mennyire van ismétlődés ebben az
ezer hexa jegyes szekvenciában önmagában.
A következő TCAG2Hexa osztálybeli rövid kóddal az előző pontban említett emberi 2. kromoszóma genomjának
egy részét hexadecimális számjegyekké alakítva összevethetjük a humán genom e részét például a Pi
hexadecimális kifejtésének számjegyeivel!
A TCAG2Hexa osztály a bemenetéről olvasott T, C, A, G betűket kettessével - mert 4 féle * 4 féle éppen 16 -
hexa számjegyekké alakítja:
public class TCAG2Hexa {
public static void main(String[] args) throws Exception {
Character hexaJegyek[] = {'A', 'B', 'C', 'D', 'E', 'F'};
boolean páratlan = false;
int első=0, második=0;
int i = 0;
while((i=System.in.read()) != -1) {
switch(i) {
case 'T':
második = 0;
break;
case 'C':
második = 1;
Page 326
Számítási mellékletek
298 Created by XMLmind XSL-FO Converter.
break;
case 'A':
második = 2;
break;
case 'G':
második = 3;
break;
}
if(páratlan) {
int jegy = 4*első + második;
if(jegy<10)
System.out.print(jegy);
else
System.out.print(hexaJegyek[jegy-10]);
}
páratlan = !páratlan;
első = második;
}
}
}
A hexába konvertáló TCAG2Hexa osztályt fordítva és futtatáskor a humán genom említett, most 1875 betűt részét
tartalmazó 2cgenom1875.fasta állomány tartalmát beleirányítva a kimenetet a genom.hexa állományban
kapjuk.
C:\...> javac TCAG2Hexa.java
C:\...> java TCAG2Hexa <2cgenom1875.fasta > genom.hexa
A kapott genom.hexa állományt és a Pi első ezer jegyét tartalmazó állományt adjuk majd át a pontmátrix
programunknak. Ez persze egy erősen spekulatív példánk volt, de ha kirajzolódott volna egy átlós egyenes a
pontmártixon, akkor talán hasonló élményünk lehetett volna, mint Mandelbrotnak, amikor rábukkant híres
halmazára.
1.1.2. A Swinges felület építésének alapszabálya
A Swinges felületek építésének alapszabálya, hogy ne csináltassunk időigényes dolgokat az
eseménykezelőkben, mivel az eseménykezelést és a megjelenítést ugyanaz a programszál végzi, így ha az
eseménykezelőt egy hosszadalmas számítással blokkoljuk, akkor ezzel egyben a felület megjelenítését is
blokkoljuk, azaz lefagyasztjuk a programunk felületét!
Szerezzünk most konkrét tapasztalatot az említett szabállyal kapcsolatban! Például pontmátrix számoló
programunknak inputként adjuk meg a 2. emberi kromoszóma egy hozzávetőlegesen 2000 (T, C, A, G)
nukleotid betűs részletét, az egyik és a másik mintaként is. Ebben az esetben a szekvenciában az ismétlődő
részeket tudjuk megfigyelni. Az érdeklődő Olvasó a Humán Genom Projekt keretében meghatározott emberi
DNS adatokat a http://www.ncbi.nlm.nih.gov/genome/seq címen találhatja meg. Erre a nagy, közel 2000x2000
pixel méretű képre a Szűrés menüpontot alkalmazva, amíg a szűrés be nem fejeződik, addig a felület lefagyott
állapotban van, nem frissül és felhasználói eseményekre sem reagál, azaz hiába kattintunk például a Névjegy
menüpontra, semmi nem történik, amíg be nem fejeződik a szűrés.
Page 327
Számítási mellékletek
299 Created by XMLmind XSL-FO Converter.
1.2. Sejtautomata szimuláció programja
Egy konkrét sejtautomatával - talán a legismertebb ilyennel - a John Horton Conway-féle életjátékkal ([MATEK
JÁTÉK], [ÉLET CIKK]) foglalkozunk részletesen. Itt egy sejt egy sejttér eleme, a sejt állapota lehet élő vagy
halott. A diszkrét időben működő sejttér adott sejtjének állapotát a következő időpillanatban a következő
átmeneti szabályok alapján számolhatjuk ki:
• Élő sejt élő marad, ha kettő vagy három élő szomszédja van, különben halott lesz.
• Halott sejt halott marad, ha három élő szomszédja van, különben élő lesz.
Ezeket az átmeneti szabályokat az időFejlődés() függvényben fogalmaztuk meg:
public void időFejlődés() {
boolean [][] rácsElőtte = rácsok[rácsIndex];
boolean [][] rácsUtána = rácsok[(rácsIndex+1)%2];
for(int i=0; i<rácsElőtte.length; ++i) { // sorok
for(int j=0; j<rácsElőtte[0].length; ++j) { // oszlopok
int élők = szomszédokSzáma(rácsElőtte, i, j, ÉLŐ);
if(rácsElőtte[i][j] == ÉLŐ) {
/* Élő élő marad, ha kettő vagy három élő
Page 328
Számítási mellékletek
300 Created by XMLmind XSL-FO Converter.
szomszedja van, különben halott lesz. */
if(élők==2 || élők==3)
rácsUtána[i][j] = ÉLŐ;
else
rácsUtána[i][j] = HALOTT;
} else {
/* Halott halott marad, ha három élő
szomszedja van, különben élő lesz. */
if(élők==3)
rácsUtána[i][j] = ÉLŐ;
else
rácsUtána[i][j] = HALOTT;
}
}
}
rácsIndex = (rácsIndex+1)%2;
}
/*
* Sejtautomata.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* Sejtautomata osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
*/
public class Sejtautomata extends java.awt.Frame implements Runnable {
/** Egy sejt lehet élő */
public static final boolean ÉLŐ = true;
/** vagy halott */
public static final boolean HALOTT = false;
/** Két rácsot használunk majd, az egyik a sejttér állapotát
* a t_n, a másik a t_n+1 időpillanatban jellemzi. */
protected boolean [][][] rácsok = new boolean [2][][];
/** Valamelyik rácsra mutat, technikai jellegű, hogy ne kelljen a
* [2][][]-ból az első dimenziót használni, mert vagy az egyikre
* állítjuk, vagy a másikra. */
protected boolean [][] rács;
/** Megmutatja melyik rács az aktuális: [rácsIndex][][] */
protected int rácsIndex = 0;
/** Pixelben egy cella adatai. */
protected int cellaSzélesség = 20;
protected int cellaMagasság = 20;
/** A sejttér nagysága, azaz hányszor hány cella van? */
protected int szélesség = 20;
protected int magasság = 10;
/** A sejttér két egymást követő t_n és t_n+1 diszkrét időpillanata
közötti valós idő. */
protected int várakozás = 1000;
// Pillanatfelvétel készítéséhez
private java.awt.Robot robot;
/** Készítsünk pillanatfelvételt? */
private boolean pillanatfelvétel = false;
/** A pillanatfelvételek számozásához. */
private static int pillanatfelvételSzámláló = 0;
/**
* Létrehoz egy <code>Sejtautomata</code> objektumot.
*
* @param szélesség a sejttér szélessége.
* @param magasság a sejttér szélessége.
*/
public Sejtautomata(int szélesség, int magasság) {
Page 329
Számítási mellékletek
301 Created by XMLmind XSL-FO Converter.
this.szélesség = szélesség;
this.magasság = magasság;
// A két rács elkészítése
rácsok[0] = new boolean[magasság][szélesség];
rácsok[1] = new boolean[magasság][szélesség];
rácsIndex = 0;
rács = rácsok[rácsIndex];
// A kiinduló rács minden cellája HALOTT
for(int i=0; i<rács.length; ++i)
for(int j=0; j<rács[0].length; ++j)
rács[i][j] = HALOTT;
// A kiinduló rácsra "élőlényeket" helyezünk
//sikló(rács, 2, 2);
siklóKilövő(rács, 5, 60);
// Az ablak bezárásakor kilépünk a programból.
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
setVisible(false);
System.exit(0);
}
});
// A billentyűzetről érkező események feldolgozása
addKeyListener(new java.awt.event.KeyAdapter() {
// Az 'k', 'n', 'l', 'g' és 's' gombok lenyomását figyeljük
public void keyPressed(java.awt.event.KeyEvent e) {
if(e.getKeyCode() == java.awt.event.KeyEvent.VK_K) {
// Felezük a cella méreteit:
cellaSzélesség /= 2;
cellaMagasság /= 2;
setSize(Sejtautomata.this.szélesség*cellaSzélesség,
Sejtautomata.this.magasság*cellaMagasság);
validate();
} else if(e.getKeyCode() == java.awt.event.KeyEvent.VK_N) {
// Duplázzuk a cella méreteit:
cellaSzélesség *= 2;
cellaMagasság *= 2;
setSize(Sejtautomata.this.szélesség*cellaSzélesség,
Sejtautomata.this.magasság*cellaMagasság);
validate();
} else if(e.getKeyCode() == java.awt.event.KeyEvent.VK_S)
pillanatfelvétel = !pillanatfelvétel;
else if(e.getKeyCode() == java.awt.event.KeyEvent.VK_G)
várakozás /= 2;
else if(e.getKeyCode() == java.awt.event.KeyEvent.VK_L)
várakozás *= 2;
repaint();
}
});
// Egér kattintó események feldolgozása:
addMouseListener(new java.awt.event.MouseAdapter() {
// Egér kattintással jelöljük ki a nagyítandó területet
// bal felső sarkát vagy ugyancsak egér kattintással
// vizsgáljuk egy adott pont iterációit:
public void mousePressed(java.awt.event.MouseEvent m) {
// Az egérmutató pozíciója
int x = m.getX()/cellaSzélesség;
int y = m.getY()/cellaMagasság;
rácsok[rácsIndex][y][x] = !rácsok[rácsIndex][y][x];
repaint();
}
});
// Egér mozgás események feldolgozása:
addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
// Vonszolással jelöljük ki a négyzetet:
public void mouseDragged(java.awt.event.MouseEvent m) {
int x = m.getX()/cellaSzélesség;
int y = m.getY()/cellaMagasság;
rácsok[rácsIndex][y][x] = ÉLŐ;
repaint();
}
});
// Cellaméretek kezdetben
Page 330
Számítási mellékletek
302 Created by XMLmind XSL-FO Converter.
cellaSzélesség = 10;
cellaMagasság = 10;
// Pillanatfelvétel készítéséhez:
try {
robot = new java.awt.Robot(
java.awt.GraphicsEnvironment.
getLocalGraphicsEnvironment().
getDefaultScreenDevice());
} catch(java.awt.AWTException e) {
e.printStackTrace();
}
// A program ablakának adatai:
setTitle("Sejtautomata");
setResizable(false);
setSize(szélesség*cellaSzélesség,
magasság*cellaMagasság);
setVisible(true);
// A sejttér életrekeltése:
new Thread(this).start();
}
/** A sejttér kirajzolása. */
public void paint(java.awt.Graphics g) {
// Az aktuális
boolean [][] rács = rácsok[rácsIndex];
// rácsot rajzoljuk ki:
for(int i=0; i<rács.length; ++i) { // végig lépked a sorokon
for(int j=0; j<rács[0].length; ++j) { // s az oszlopok
// Sejt cella kirajzolása
if(rács[i][j] == ÉLŐ)
g.setColor(java.awt.Color.BLACK);
else
g.setColor(java.awt.Color.WHITE);
g.fillRect(j*cellaSzélesség, i*cellaMagasság,
cellaSzélesség, cellaMagasság);
// Rács kirajzolása
g.setColor(java.awt.Color.LIGHT_GRAY);
g.drawRect(j*cellaSzélesség, i*cellaMagasság,
cellaSzélesség, cellaMagasság);
}
}
// Készítünk pillanatfelvételt?
if(pillanatfelvétel) {
// a biztonság kedvéért egy kép készítése után
// kikapcsoljuk a pillanatfelvételt, hogy a
// programmal ismerkedő Olvasó ne írja tele a
// fájlrendszerét a pillanatfelvételekkel
pillanatfelvétel = false;
pillanatfelvétel(robot.createScreenCapture
(new java.awt.Rectangle
(getLocation().x, getLocation().y,
szélesség*cellaSzélesség,
magasság*cellaMagasság)));
}
}
/**
* Az kérdezett állapotban lévő nyolcszomszédok száma.
*
* @param rács a sejttér rács
* @param sor a rács vizsgált sora
* @param oszlop a rács vizsgált oszlopa
* @param állapor a nyolcszomszédok vizsgált állapota
* @return int a kérdezett állapotbeli nyolcszomszédok száma.
*/
public int szomszédokSzáma(boolean [][] rács,
int sor, int oszlop, boolean állapot) {
int állapotúSzomszéd = 0;
// A nyolcszomszédok végigzongorázása:
for(int i=-1; i<2; ++i)
for(int j=-1; j<2; ++j)
// A vizsgált sejtet magát kihagyva:
if(!((i==0) && (j==0))) {
// A sejttérből szélének szomszédai
Page 331
Számítási mellékletek
303 Created by XMLmind XSL-FO Converter.
// a szembe oldalakon ("periódikus határfeltétel")
int o = oszlop + j;
if(o < 0)
o = szélesség-1;
else if(o >= szélesség)
o = 0;
int s = sor + i;
if(s < 0)
s = magasság-1;
else if(s >= magasság)
s = 0;
if(rács[s][o] == állapot)
++állapotúSzomszéd;
}
return állapotúSzomszéd;
}
/**
* A sejttér időbeli fejlődése a John H. Conway féle
* életjáték sejtautomata szabályai alapján történik.
* A szabályok részletes ismertetését lásd például a
* [MATEK JÁTÉK] hivatkozásban (Csákány Béla: Diszkrét
* matematikai játékok. Polygon, Szeged 1998. 171. oldal.)
*/
public void időFejlődés() {
boolean [][] rácsElőtte = rácsok[rácsIndex];
boolean [][] rácsUtána = rácsok[(rácsIndex+1)%2];
for(int i=0; i<rácsElőtte.length; ++i) { // sorok
for(int j=0; j<rácsElőtte[0].length; ++j) { // oszlopok
int élők = szomszédokSzáma(rácsElőtte, i, j, ÉLŐ);
if(rácsElőtte[i][j] == ÉLŐ) {
/* Élő élő marad, ha kettő vagy három élő
szomszedja van, különben halott lesz. */
if(élők==2 || élők==3)
rácsUtána[i][j] = ÉLŐ;
else
rácsUtána[i][j] = HALOTT;
} else {
/* Halott halott marad, ha három élő
szomszedja van, különben élő lesz. */
if(élők==3)
rácsUtána[i][j] = ÉLŐ;
else
rácsUtána[i][j] = HALOTT;
}
}
}
rácsIndex = (rácsIndex+1)%2;
}
/** A sejttér időbeli fejlődése. */
public void run() {
while(true) {
try {
Thread.sleep(várakozás);
} catch (InterruptedException e) {}
időFejlődés();
repaint();
}
}
/**
* A sejttérbe "élőlényeket" helyezünk, ez a "sikló".
* Adott irányban halad, másolja magát a sejttérben.
* Az élőlény ismertetését lásd például a
* [MATEK JÁTÉK] hivatkozásban (Csákány Béla: Diszkrét
Page 332
Számítási mellékletek
304 Created by XMLmind XSL-FO Converter.
* matematikai játékok. Polygon, Szeged 1998. 172. oldal.)
*
* @param rács a sejttér ahová ezt az állatkát helyezzük
* @param x a befoglaló tégla bal felső sarkának oszlopa
* @param y a befoglaló tégla bal felső sarkának sora
*/
public void sikló(boolean [][] rács, int x, int y) {
rács[y+ 0][x+ 2] = ÉLŐ;
rács[y+ 1][x+ 1] = ÉLŐ;
rács[y+ 2][x+ 1] = ÉLŐ;
rács[y+ 2][x+ 2] = ÉLŐ;
rács[y+ 2][x+ 3] = ÉLŐ;
}
/**
* A sejttérbe "élőlényeket" helyezünk, ez a "sikló ágyú".
* Adott irányban siklókat lő ki.
* Az élőlény ismertetését lásd például a
* [MATEK JÁTÉK] hivatkozásban /Csákány Béla: Diszkrét
* matematikai játékok. Polygon, Szeged 1998. 173. oldal./,
* de itt az ábra hibás, egy oszloppal told még balra a
* bal oldali 4 sejtes négyzetet. A helyes ágyú rajzát
* lásd pl. az [ÉLET CIKK] hivatkozásban /Robert T.
* Wainwright: Life is Universal./ (Megemlíthetjük, hogy
* mindkettő tartalmaz két felesleges sejtet is.)
*
* @param rács a sejttér ahová ezt az állatkát helyezzük
* @param x a befoglaló tégla bal felső sarkának oszlopa
* @param y a befoglaló tégla bal felső sarkának sora
*/
public void siklóKilövő(boolean [][] rács, int x, int y) {
rács[y+ 6][x+ 0] = ÉLŐ;
rács[y+ 6][x+ 1] = ÉLŐ;
rács[y+ 7][x+ 0] = ÉLŐ;
rács[y+ 7][x+ 1] = ÉLŐ;
rács[y+ 3][x+ 13] = ÉLŐ;
rács[y+ 4][x+ 12] = ÉLŐ;
rács[y+ 4][x+ 14] = ÉLŐ;
rács[y+ 5][x+ 11] = ÉLŐ;
rács[y+ 5][x+ 15] = ÉLŐ;
rács[y+ 5][x+ 16] = ÉLŐ;
rács[y+ 5][x+ 25] = ÉLŐ;
rács[y+ 6][x+ 11] = ÉLŐ;
rács[y+ 6][x+ 15] = ÉLŐ;
rács[y+ 6][x+ 16] = ÉLŐ;
rács[y+ 6][x+ 22] = ÉLŐ;
rács[y+ 6][x+ 23] = ÉLŐ;
rács[y+ 6][x+ 24] = ÉLŐ;
rács[y+ 6][x+ 25] = ÉLŐ;
rács[y+ 7][x+ 11] = ÉLŐ;
rács[y+ 7][x+ 15] = ÉLŐ;
rács[y+ 7][x+ 16] = ÉLŐ;
rács[y+ 7][x+ 21] = ÉLŐ;
rács[y+ 7][x+ 22] = ÉLŐ;
rács[y+ 7][x+ 23] = ÉLŐ;
rács[y+ 7][x+ 24] = ÉLŐ;
rács[y+ 8][x+ 12] = ÉLŐ;
rács[y+ 8][x+ 14] = ÉLŐ;
rács[y+ 8][x+ 21] = ÉLŐ;
rács[y+ 8][x+ 24] = ÉLŐ;
rács[y+ 8][x+ 34] = ÉLŐ;
rács[y+ 8][x+ 35] = ÉLŐ;
rács[y+ 9][x+ 13] = ÉLŐ;
Page 333
Számítási mellékletek
305 Created by XMLmind XSL-FO Converter.
rács[y+ 9][x+ 21] = ÉLŐ;
rács[y+ 9][x+ 22] = ÉLŐ;
rács[y+ 9][x+ 23] = ÉLŐ;
rács[y+ 9][x+ 24] = ÉLŐ;
rács[y+ 9][x+ 34] = ÉLŐ;
rács[y+ 9][x+ 35] = ÉLŐ;
rács[y+ 10][x+ 22] = ÉLŐ;
rács[y+ 10][x+ 23] = ÉLŐ;
rács[y+ 10][x+ 24] = ÉLŐ;
rács[y+ 10][x+ 25] = ÉLŐ;
rács[y+ 11][x+ 25] = ÉLŐ;
}
/** Pillanatfelvételek készítése. */
public void pillanatfelvétel(java.awt.image.BufferedImage felvetel) {
// A pillanatfelvétel kép fájlneve
StringBuffer sb = new StringBuffer();
sb = sb.delete(0, sb.length());
sb.append("sejtautomata");
sb.append(++pillanatfelvételSzámláló);
sb.append(".png");
// png formátumú képet mentünk
try {
javax.imageio.ImageIO.write(felvetel, "png",
new java.io.File(sb.toString()));
} catch(java.io.IOException e) {
e.printStackTrace();
}
}
// Ne villogjon a felület (mert a "gyári" update()
// lemeszelné a vászon felületét).
public void update(java.awt.Graphics g) {
paint(g);
}
/**
* Példányosít egy Conway-féle életjáték szabályos
* sejttér obektumot.
*/
public static void main(String[] args) {
// 100 oszlop, 75 sor mérettel:
new Sejtautomata(100, 75);
}
}
Könnyen végezhetünk saját szimulációkat az osztály imént megadott kódjával. Jelöljük ki a forrásszöveget és
illesszük be egy Sejtautomata.java nevű állományba, majd fordítsuk az állományt és futtassuk a számítást:
C:\...> javac Sejtautomata.java
C:\...> java Sejtautomata
A program futása alatt az s billentyűt lenyomva a sejttér aktuális pillanatbeli állapotáról egy felvételt készít a
program. A készített képeket abban a könyvtárban találjuk, ahonnan a programot a fent ajánlott módon
elindítottuk.
Korábbi tárgyalásunknak megfelelően a program a következő inputokat dolgoza fel:
• Az s billentyű lenyomásával egy felvétel kép készül a sejttér aktuális állapotáról.
• Az n billentyű lenyomásával növeljük a sejtek méretét.
• A k billentyű lenyomásával csökkentjük a sejtek méretét.
Page 334
Számítási mellékletek
306 Created by XMLmind XSL-FO Converter.
• A g billentyű lenyomásával csökkentjük a sejttér két diszkrét állapota közötti valóságos időt, azaz gyorsítjuk a
szimulációt.
• Az l billentyű lenyomásával lassítjuk a szimulációt.
• Az egérmutató jobb vagy bal gombjával egy sejt állapotát az ellenkezőjére változtatjuk.
• Az egérmutató vonszolásával az érintett sejteket élő állapotba kapcsoljuk.
A következő ábrasorozaton bemutatjuk az R. W. Gosper találta [MATEK JÁTÉK], [ÉLET CIKK] „sikló ágyú”
élőlényt üzem közben (az első hivatkozásban közölt ágyú picit hibás, ezért csak három lövést ad le, de mindkét
hivatkozás tartalmaz két felesleges sejtet).
B.1. táblázat - Az R. W. Gosper-féle sikló ágyú élőlény bemutatása
Page 335
Számítási mellékletek
307 Created by XMLmind XSL-FO Converter.
Page 336
Számítási mellékletek
308 Created by XMLmind XSL-FO Converter.
Page 337
Számítási mellékletek
309 Created by XMLmind XSL-FO Converter.
Page 338
Számítási mellékletek
310 Created by XMLmind XSL-FO Converter.
Page 339
Számítási mellékletek
311 Created by XMLmind XSL-FO Converter.
Page 340
Számítási mellékletek
312 Created by XMLmind XSL-FO Converter.
1.3. Orch OR demonstrációk
A mikrotubulus sejtautomata - szemben az előző pont példájával - nem négy vagy nyolc szomszéddal dolgozik,
hanem amint a Hameroff mikrotubulus sejtautomatái című pontban megmutattuk, egy hexagonális rácson, ahol
egy adott sejtnek hat szomszédja van.
1.3.1. Hexagonális rács
Ilyen hexagonális rácsot geometriailag könnyen kaphatunk, ha klasszikus tömbünket (rácsunkat) lerajzoljuk és
minden második oszlopát egy rajzolt cella felével eltoljuk. Azért használjuk ezt a triviális szemléletbeli képet,
mert a klasszikus tömbök kezelése - lévén, hogy elemei a Java nyelvnek - egyszerű.
Page 341
Számítási mellékletek
313 Created by XMLmind XSL-FO Converter.
Tehát, ha klasszikus tömböket akarunk használni a szimuláció
programozásánál, csak arra kell figyelni, hogy az eltolt rácson lévő adott sejt hat szomszédai kik lesznek a
klasszikusra visszatolt rácson?
Az ábráról
leolvasható, hogy triviálisan tudunk szokásos tömböket használni, amiket majd a megjelenítés során, második
oszloponként eltolva rajzolunk ki. A szomszédok pozíciója pedig attól függ, hogy eltolt vagy nem eltolt
oszlopban van-e a vizsgált sejt.
1.3.1.1. A Mikrotubulus osztály
GUI programozási szempontból tartjuk fontosnak megjegyezni, hogy a Mikrotubulus osztály közvetlenül
önmagára rajzol, jobb gyakorlat egy az ablakra helyezett vászonra vagy képre rajzolni. Az utóbbira például a
kézikönyv Mandelbrot halmaz című pontjában láthatunk példát. Itt a MandelbrotHalmaz osztály
pillanatfelvétel() pillanatfelvételt készítő függvényében használjuk ki például a képre rajzolást.
Page 342
Számítási mellékletek
314 Created by XMLmind XSL-FO Converter.
/*
* Mikrotubulus.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* Orch OR sejtautomata demonstráció.
* A jelen szimulációs vagy pontosabban csupán - az [ORCH OR] Stuart
* Hameroff és Roger Penrose, Orchestrated Objective Reduction of
* Quantum Coherence in Brain Microtubules: The “Orch OR” Model for
* Consciousmess,
* http://www.quantumconsciousness.org/penrose-hameroff/orchOR.html
* [ORCH OR TUDAT] Stuart Hameroff és Roger Penrose, Conscious Events
* as Orchestrated Space-Time Selections,
* http://www.quantumconsciousness.org/penrosehameroff/consciousevents.html
* cikkek képei alapján készített - demonstrációs osztály azt mutatja,
* hogy egyre több tubulin dimer kerül koherens állapotba, míg az
* objektív redukció következtében egy előre nem kiszámítható módon
* ugranak a résztvevő cellák valamely lehetséges állapotukba,
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
*/
public class Mikrotubulus extends java.awt.Frame implements Runnable {
/** Rácspont az egyik állapotában. */
public static final int EGYIK = 0;
/** Rácspont a másik állapotában. */
public static final int MÁSIK = 1;
/** Rácspont az összefonódott állapotban. */
public static final int SZUPER = 2;
/** Hány összefonódott rácspont indítja be az OR-t? */
public static final int EGY_GRAVITON_SZINTNYI = 235;
/** Két ráccsal dolgozunk: a diszkrét időskála adott
* pillanata előtti és utáni rácsot írjuk le velük. */
int [][][] hexagonálisRácsok = new int [2][][];
/** Melyik az éppen aktuális rács? */
int [][] hexagonálisRács;
/** A 0. vagy az 1. ? */
int rácsIndex = 0;
/* A kirajzolás adatai: */
int cellaSzélesség = 20;
int cellaMagasság = 20;
/* A rács adatai: */
int szélesség = 13;
int magasság = 20;
// Technikai:
java.util.Random random = new java.util.Random();
/* Képek a kirajzoláshoz */
java.awt.Image fehérKép;
java.awt.Image feketeKép;
java.awt.Image pirosKép;
// A pillanatfelvételekhez, technikai:
java.awt.Robot robot;
/**
* Az adott méretű {@code Mikrotubulus} objektumot
* felépítő konstruktor.
*
* @param szélesség tubulin-dimerek száma vízszintesen
* @param magasság tubulin-dimerek száma függőlegesen
*/
public Mikrotubulus(int szélesség, int magasság) {
this.szélesség = szélesség;
this.magasság = magasság;
// A diszkrét időskála adott
// pillanata előtti és utáni rácsok:
hexagonálisRácsok[0] = new int[magasság][szélesség];
hexagonálisRácsok[1] = new int[magasság][szélesség];
Page 343
Számítási mellékletek
315 Created by XMLmind XSL-FO Converter.
hexagonálisRács = hexagonálisRácsok[rácsIndex];
// a rácspontok egy véletlen állapotából indul a sejtautomata
for(int i=0; i<hexagonálisRács.length; ++i)
for(int j=0; j<hexagonálisRács[0].length; ++j)
hexagonálisRács[i][j] = random.nextInt(2);
// innen indul ki egy összefonódás
hexagonálisRács[10][6] = SZUPER;
// a program ablaka, amit be is lehet csukni
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
setVisible(false);
System.exit(0);
}
});
// Képek betöltése
fehérKép = new javax.swing.ImageIcon
("fehér.png").getImage();
feketeKép = new javax.swing.ImageIcon
("fekete.png").getImage();
pirosKép = new javax.swing.ImageIcon
("piros.png").getImage();
cellaSzélesség = fehérKép.getWidth(this);
cellaMagasság = fehérKép.getHeight(this);
// a lokális grafikus környezet elkérése
java.awt.GraphicsEnvironment graphicsEnvironment
= java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment();
// a grafikus környzetből a képernyővel dolgozunk
java.awt.GraphicsDevice graphicsDevice
= graphicsEnvironment.getDefaultScreenDevice();
try {
robot = new java.awt.Robot(graphicsDevice);
} catch(java.awt.AWTException e) {
e.printStackTrace();
}
// Ablak jellemzőinek beállítása
setTitle("Mikrotubulus");
setResizable(false);
//setUndecorated(true);
setSize(szélesség*cellaSzélesség,
magasság*cellaMagasság);
setVisible(true);
// Szimulációs (demonstrációs) szál elkészítése és beindítása
new Thread(this).start();
}
/* A felület kirajzolása */
public void paint(java.awt.Graphics g) {
// Éppen melyik rácsot kell rajzolnunk:
int [][] hexagonálisRács = hexagonálisRácsok[rácsIndex];
// "Töröljük" a felületet (ha villog, töröljünk a
// következő ciklusban kis darabokat)
g.setColor(java.awt.Color.GRAY);
g.fillRect(0,0, szélesség*cellaSzélesség,
magasság*cellaMagasság);
// Végigmegyünk a rácspontokon:
for(int i=0; i<hexagonálisRács.length; ++i) { // sorok
for(int j=0; j<hexagonálisRács[0].length; ++j) { // oszlopok
/* Csak a rácsot rajzolja
g.setColor(java.awt.Color.BLACK);
g.drawRect(j*20, i*20+(j%2)*10, 20, 20);
Csak a rácsot rajzolja */
/* Nem képeket, színes téglalapokat rajzol
Page 344
Számítási mellékletek
316 Created by XMLmind XSL-FO Converter.
if(hexagonálisRács[i][j] == EGYIK)
g.setColor(java.awt.Color.BLACK);
else if(hexagonálisRács[i][j] == MÁSIK)
g.setColor(java.awt.Color.WHITE);
else
g.setColor(java.awt.Color.RED);
// a +(j%2)*10 a páratlanadik oszlopokat
// lefelé tolja
g.fillRect(j*20, i*20+(j%2)*10, 20, 20);
Nem képeket, színes téglalapokat rajzol */
// A szimulációs képeket rajzolja
if(hexagonálisRács[i][j] == EGYIK)
g.drawImage(fehérKép, j*fehérKép.getWidth(this),
i*fehérKép.getHeight(this)
+(j%2)*(fehérKép.getHeight(this)/2), null);
else if(hexagonálisRács[i][j] == MÁSIK)
g.drawImage(feketeKép, j*feketeKép.getWidth(this),
i*feketeKép.getHeight(this)
+(j%2)*(fehérKép.getHeight(this)/2), null);
else
g.drawImage(pirosKép, j*pirosKép.getWidth(this),
i*pirosKép.getHeight(this)
+(j%2)*(fehérKép.getHeight(this)/2), null);
// A szimulációs képeket rajzolja
}
}
boolean pillanatfelvetel = false;
if(pillanatfelvetel) {
pillanatfelvetel = false;
pillanatfelvetel(robot.createScreenCapture
(new java.awt.Rectangle
(0, 0, szélesség*cellaSzélesség,
magasság*cellaMagasság)));
}
}
/* Technikai a villogás ellen (nem meszeli le a hátteret az update(),
mert most felüldefiniáljuk meszelés nélkül). */
public void update(java.awt.Graphics g) {
paint(g);
}
// A pillanatfelvételek számozásához
public static int fotoSzamlalo = 0;
/** ScreenShot készítése. */
public void pillanatfelvetel(java.awt.image.BufferedImage felvetel) {
// tech. a sztringezéshez: a képfájl nevének előállítása
StringBuffer sb = new StringBuffer();
sb = sb.delete(0, sb.length());
sb.append("mikrotubulus");
sb.append(++fotoSzamlalo);
sb.append(".png");
// a kép mentése
try {
// png-t mentünk
javax.imageio.ImageIO.write(felvetel, "png",
new java.io.File(sb.toString()));
} catch(java.io.IOException e) {
e.printStackTrace();
}
}
/**
* A rács adott cellájának hány szomszéda van a kérdezett állapotban?
*/
public int szomszédokSzáma(int [][] hexagonálisRács,
int sor, int oszlop, int állapot) {
Page 345
Számítási mellékletek
317 Created by XMLmind XSL-FO Converter.
int állapotúSzomszéd = 0;
if(oszlop%2 == 1) {
if(sor-1>0)
if(hexagonálisRács[sor-1][oszlop] == állapot)
++állapotúSzomszéd;
if(sor+1<magasság)
if(hexagonálisRács[sor+1][oszlop] == állapot)
++állapotúSzomszéd;
if(oszlop-1>0) {
if(hexagonálisRács[sor][oszlop-1] == állapot)
++állapotúSzomszéd;
if(sor+1<magasság)
if(hexagonálisRács[sor+1][oszlop-1] == állapot)
++állapotúSzomszéd;
}
if(oszlop+1<szélesség) {
if(hexagonálisRács[sor][oszlop+1] == állapot)
++állapotúSzomszéd;
if(sor+1<magasság)
if(hexagonálisRács[sor+1][oszlop+1] == állapot)
++állapotúSzomszéd;
}
} else {
if(sor-1>0)
if(hexagonálisRács[sor-1][oszlop] == állapot)
++állapotúSzomszéd;
if(sor+1<magasság)
if(hexagonálisRács[sor+1][oszlop] == állapot)
++állapotúSzomszéd;
if(oszlop-1>0) {
if(hexagonálisRács[sor][oszlop-1] == állapot)
++állapotúSzomszéd;
if(sor-1>0)
if(hexagonálisRács[sor-1][oszlop-1] == állapot)
++állapotúSzomszéd;
}
if(oszlop+1<szélesség) {
if(hexagonálisRács[sor][oszlop+1] == állapot)
++állapotúSzomszéd;
if(sor-1>0)
if(hexagonálisRács[sor-1][oszlop+1] == állapot)
++állapotúSzomszéd;
}
}
return állapotúSzomszéd;
}
/** Van összefonódott állapotban lévő szomszédja a rács
* adott cellájának? */
public boolean vanSzuperSzomszéd(int [][] hexagonálisRács,
int sor, int oszlop) {
if(oszlop%2 == 1) {
if(sor-1>0)
if(hexagonálisRács[sor-1][oszlop] == SZUPER)
return true;
if(sor+1<magasság)
if(hexagonálisRács[sor+1][oszlop] == SZUPER)
return true;
if(oszlop-1>0) {
if(hexagonálisRács[sor][oszlop-1] == SZUPER)
Page 346
Számítási mellékletek
318 Created by XMLmind XSL-FO Converter.
return true;
if(sor+1<magasság)
if(hexagonálisRács[sor+1][oszlop-1] == SZUPER)
return true;
}
if(oszlop+1<szélesség) {
if(hexagonálisRács[sor][oszlop+1] == SZUPER)
return true;
if(sor+1<magasság)
if(hexagonálisRács[sor+1][oszlop+1] == SZUPER)
return true;
}
} else {
if(sor-1>0)
if(hexagonálisRács[sor-1][oszlop] == SZUPER)
return true;
if(sor+1<magasság)
if(hexagonálisRács[sor+1][oszlop] == SZUPER)
return true;
if(oszlop-1>0) {
if(hexagonálisRács[sor][oszlop-1] == SZUPER)
return true;
if(sor-1>0)
if(hexagonálisRács[sor-1][oszlop-1] == SZUPER)
return true;
}
if(oszlop+1<szélesség) {
if(hexagonálisRács[sor][oszlop+1] == SZUPER)
return true;
if(sor-1>0)
if(hexagonálisRács[sor-1][oszlop+1] == SZUPER)
return true;
}
}
return false;
}
/** A szimuláció időfejlődése, diszkrét időskálán dolgozunk. */
public void időFejlődés() {
/* Két ráccsal dolgozunk: a diszkrét időskála adott
pillanata előtti és utáni ráccsal: */
int [][] hexagonálisRácsElőtte = hexagonálisRácsok[rácsIndex];
int [][] hexagonálisRácsUtána = hexagonálisRácsok[(rácsIndex+1)%2];
// Hány összefonódott van ebben az időpillanatban?
int szuperszámláló = 0;
for(int i=0; i<hexagonálisRácsElőtte.length; ++i) { // sorok
for(int j=0; j<hexagonálisRácsElőtte[0].length; ++j) { // oszlopok
if(hexagonálisRácsElőtte[i][j] == SZUPER) {
// Összefonódott az is marad
hexagonálisRácsUtána[i][j] = hexagonálisRácsElőtte[i][j];
++szuperszámláló;
} else {
// Egyébként a következő, hasraütésre vett
// átmeneti szabályokat alkalmazzuk:
if(vanSzuperSzomszéd(hexagonálisRácsElőtte, i, j)) {
if(random.nextInt(4) > 0) {
hexagonálisRácsUtána[i][j] = SZUPER;
++szuperszámláló;
}
Page 347
Számítási mellékletek
319 Created by XMLmind XSL-FO Converter.
} else if(random.nextInt(125) == 0) {
hexagonálisRácsUtána[i][j] = SZUPER;
++szuperszámláló;
} else {
int egyik = szomszédokSzáma(hexagonálisRácsElőtte,
i, j, EGYIK);
int másik = szomszédokSzáma(hexagonálisRácsElőtte,
i, j, MÁSIK);
if(hexagonálisRácsElőtte[i][j] == EGYIK) {
if(egyik == másik)
hexagonálisRácsUtána[i][j] = MÁSIK;
} else {
if(másik < egyik)
hexagonálisRácsUtána[i][j] = EGYIK;
else
hexagonálisRácsUtána[i][j] = MÁSIK;
}
}
}
// ha elértük az egy graviton szintet, akkor
// bekövetkezik
if(szuperszámláló >= EGY_GRAVITON_SZINTNYI) {
// az OR folyamat:
állapotvektorRedukció();
szuperszámláló = 0;
}
}
}
// a régi rács lesz az új:
rácsIndex = (rácsIndex+1)%2;
}
/** Az OR folyamat demonstrációja. */
public void állapotvektorRedukció() {
int [][] hexagonálisRács1 = hexagonálisRácsok[rácsIndex];
for(int i=0; i<hexagonálisRács.length; ++i)
for(int j=0; j<hexagonálisRács[0].length; ++j) {
if(hexagonálisRácsok[0][i][j] == SZUPER ||
hexagonálisRácsok[1][i][j] == SZUPER) {
hexagonálisRácsok[0][i][j] = random.nextInt(2);
hexagonálisRácsok[1][i][j] = hexagonálisRácsok[0][i][j];
}
}
hexagonálisRácsok[0][2+random.nextInt(magasság-2)]
[2+random.nextInt(szélesség-2)] = SZUPER;
hexagonálisRácsok[1][2+random.nextInt(magasság-2)]
[2+random.nextInt(szélesség-2)] = SZUPER;
}
/** A szimulációs szál időfejlődése. */
public void run() {
while(true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {}
időFejlődés();
repaint();
}
Page 348
Számítási mellékletek
320 Created by XMLmind XSL-FO Converter.
}
public static void main(String[] args) {
new Mikrotubulus(13, 20);
}
}
Megjegyezhetjük, hogy ha az időfejlődésben a Thread.sleep(50); mósodított utasítást alkalmazzuk, akkor -
az általunk alkalmazott öszefonódási fejlődés mellett, ami körülbelül 10 diszkrét időpillanat - hozzávetőlegesen
fél másodpercenként látjuk bekövetkezni az OR folyamatot.
A demonstrációs programot magunk is kipróbálhatjuk. Ehhez ne fejeljtsük el a tubulin fehérjék konformációs
állapotait reprezentáló fehér.png, fekete.png és piros.png képeket a munkakönyvtárunkba másolni. Ezeket
a A példaprogramok forrásainak letöltése című pontban ismertetett és belinkelt javat_tanitok_kepek.zip
állományban találja meg a kedves Olvasó. Ezután jelöljük ki az iménti forrásszöveget és illesszük be egy
Mikrotubulus.java nevű állományba, aztán fordítsuk az állományt és futtassuk a számítást:
C:\...> javac Mikrotubulus.java
C:\...> java Mikrotubulus
Eredményül a kézikönyvben már korábban, a Mikrotubulus sejtautomata szimuláció című pontban tárgyalt
képekhez hasonlóakat kapunk.
2. Matematikai témájú programok
2.1. Galton deszka kísérlet programja
Magát a kísérletet a Galton deszka kísérlet című pontban ismertettük. Most megadjuk a kísérleti elrendezést
absztraháló, általunk készített osztály kódját is.
/*
* GaltonDeszka.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* A Galton deszka kísérletet szimuláló osztály.
* A kísérlet leírását lásd a [RÉNYI VALSÉG KÖNYV] (Rényi
* Alfréd: Valószínűségszámítás, Tankönyvkiadó, 1973, 144 o.)
* könyvben.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
*/
public class GaltonDeszka extends java.awt.Frame implements Runnable {
/** Melyik oszlopban van éppen az eső golyó? */
private int oszlop = 0;
/** Melyik sorban van éppen az eső golyó? */
private int sor = 0;
/** Hová hány golyó esett, az i. helyre hisztogram[i] */
private int [] hisztogram;
/** Hány pixel magas legyen egy deszkasor. */
private int sorMagasság;
/** Hány pixel széles legyen a kísérleti elrendezés ablaka? */
private int ablakSzélesség;
/** Hány pixel magas legyen a kísérleti elrendezés ablaka? */
private int ablakMagasság;
Page 349
Számítási mellékletek
321 Created by XMLmind XSL-FO Converter.
// Véletlenszám generátor
private java.util.Random random = new java.util.Random();
// Pillanatfelvétel készítéséhez
private java.awt.Robot robot;
/** Készítsünk pillanatfelvételt? */
private boolean pillanatfelvétel = false;
/** A pillanatfelvételek számozásához. */
private static int pillanatfelvételSzámláló = 0;
/**
* Létrehoz egy Galton deszka kísérleti elrendezést
* absztraháló <code>GaltonDeszka</code> objektumot.
*
* @param magasság a deszkasorok száma.
* @param sorMagasság a deszkasorok magassága pixelben.
*/
public GaltonDeszka(int magasság, int sorMagasság) {
// Hová hány golyó esett, az i. helyre hisztogram[i]
hisztogram = new int [magasság];
// Nullázzuk a hisztogram elemeit (nem lenne szükséges, de ez a
// biztonságos taktika)
for(int i=0; i<hisztogram.length; ++i)
hisztogram[i] = 0;
this.sorMagasság = sorMagasság;
// Az ablak bezárásakor kilépünk a programból.
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
setVisible(false);
System.exit(0);
}
});
// Az s gomb benyomásával ki/bekapcsoljuk a
// pillanatfelvétel készítést a kísérletről:
addKeyListener(new java.awt.event.KeyAdapter() {
public void keyPressed(java.awt.event.KeyEvent e) {
if(e.getKeyCode() == java.awt.event.KeyEvent.VK_S)
pillanatfelvétel = !pillanatfelvétel;
}
});
// Pillanatfelvétel készítéséhez:
try {
robot = new java.awt.Robot(
java.awt.GraphicsEnvironment.
getLocalGraphicsEnvironment().
getDefaultScreenDevice());
} catch(java.awt.AWTException e) {
e.printStackTrace();
}
// Ablak tulajdonságai
setTitle("Galton deszka kísérlet");
setResizable(false);
ablakSzélesség = magasság*sorMagasság*2;
ablakMagasság = magasság*sorMagasság+400;
setSize(ablakSzélesség, ablakMagasság);
setVisible(true);
// A kísérlet indul:
new Thread(this).start();
}
/**
* A kísérlet aktuális állapotának kirajzolása.
*/
public void paint(java.awt.Graphics g) {
// A deszkasorok és a golyó kirajzolása
for(int i=0; i<hisztogram.length; ++i) {
// Deszkák kirajzolása
g.setColor(java.awt.Color.BLACK);
for(int j=0; j<i; ++j)
g.fillRect(getWidth()/2
-((i-1)*sorMagasság+sorMagasság/2)
+j*2*sorMagasság+sorMagasság/3,
50+i*sorMagasság, sorMagasság/3, sorMagasság);
// Minden lehetséges pozícióra egy fehér
// golyó kirajzolása (törli a korábbi piros golyókat)
Page 350
Számítási mellékletek
322 Created by XMLmind XSL-FO Converter.
g.setColor(java.awt.Color.WHITE);
for(int j=0; j<=i; ++j)
g.fillArc(getWidth()/2
-(i*sorMagasság+sorMagasság/2)+j*2*sorMagasság,
50+i*sorMagasság,
sorMagasság,
sorMagasság, 0, 360);
// A most éppen aláhulló golyó kirajzolása
if(i == sor) {
g.setColor(java.awt.Color.RED);
g.fillArc(getWidth()/2
-(i*sorMagasság+sorMagasság/2)+oszlop*2*sorMagasság,
50+i*sorMagasság, sorMagasság, sorMagasság, 0, 360);
}
}
// A hisztogram kirajzolása
g.setColor(java.awt.Color.GREEN);
for(int j=0; j<hisztogram.length; ++j)
g.fillRect(getWidth()/2
-((hisztogram.length-1)*sorMagasság
+sorMagasság/2)+j*2*sorMagasság,
50+hisztogram.length*sorMagasság,
sorMagasság, sorMagasság*hisztogram[j]/10);
// Készítünk pillanatfelvételt?
if(pillanatfelvétel) {
// a biztonság kedvéért egy kép készítése után
// kikapcsoljuk a pillanatfelvételt, hogy a
// programmal ismerkedő Olvasó ne írja tele a
// fájlrendszerét a pillanatfelvételekkel
pillanatfelvétel = false;
pillanatfelvétel(robot.createScreenCapture
(new java.awt.Rectangle
(getLocation().x, getLocation().y,
ablakSzélesség, ablakMagasság)));
}
}
// Ne villogjon a felület (mert a "gyári" update()
// lemeszelné a vászon felületét).
public void update(java.awt.Graphics g) {
paint(g);
}
/**
* Pillanatfelvételek készítése.
*/
public void pillanatfelvétel(java.awt.image.BufferedImage felvetel) {
// A pillanatfelvétel kép fájlneve
StringBuffer sb = new StringBuffer();
sb = sb.delete(0, sb.length());
sb.append("GaltonDeszkaKiserlet");
sb.append(++pillanatfelvételSzámláló);
sb.append(".png");
// png formátumú képet mentünk
try {
javax.imageio.ImageIO.write(felvetel, "png",
new java.io.File(sb.toString()));
} catch(java.io.IOException e) {
e.printStackTrace();
}
}
/**
* A kísérlet időbeli fejlődésének vezérlése.
*/
public void run() {
// Végtelen ciklus, azaz végtelen sok golyót
// dobunk le a deszkák között.
while(true) {
// Kezdetben a golyó a legfelső deszka felett.
oszlop = 0;
// A ciklus minden iterációja egy deszkasornyi
// esést jelent a golyó életében
for(int i=0; i<hisztogram.length; ++i) {
Page 351
Számítási mellékletek
323 Created by XMLmind XSL-FO Converter.
// Melyik sorban van éppen az eső golyó?
sor = i;
// Ha növelni akarjuk a sebességet (a
// látvány rovására) akkor kommentezzük be
// ezt a várakozó try blokkot (de ekkor
// ne felejtsük el a hisztogram oszlopainak
// magasságát sorMagasság*hisztogram[j]/10-ről
// például sorMagasság*hisztogram[j]/10000-re állítani).
try {
Thread.sleep(50);
} catch (InterruptedException e) {}
// Az tetején a golyó az első deszka felett
if(i>0)
// ha nem a tetején, akkor 50%-50%, hogy
// jobbra vagy balra esik tovább.
// Melyik oszlopban van éppen az eső golyó?
oszlop = oszlop + random.nextInt(2);
// Rajzoljuk ki a kísérlet aktuális állapotát!
repaint();
}
// Ha kilép a golyó a ciklusból, akkor
// végig esett a deszkasorokon és valamelyik
// tárolóba esett
++hisztogram[oszlop];
}
}
/**
* Példányosít egy Galton deszkás kísérleti
* elrendezés obektumot.
*/
public static void main(String[] args) {
// Legyen 30 sor, soronként 10 pixellel
new GaltonDeszka(30, 10);
}
}
Könnyen végezhetünk saját kísérleteket az osztály imént megadott kódjával. Jelöljük ki a forrásszöveget és
illesszük be egy GaltonDeszka.java nevű állományba, majd fordítsuk az állományt és futtassuk a kísérletet:
C:\...> javac GaltonDeszka.java
C:\...> java GaltonDeszka
A program futása alatt az s billentyűt lenyomva a kísérletről egy pillanatfelvétel képet készít a program. A
készített képeket abban a könyvtárban találjuk, ahonnan a programot a fent ajánlott módon elindítottuk. Ilyen
kép például az alábbi GaltonDeszkaKiserlet1.png kép.
Page 352
Számítási mellékletek
324 Created by XMLmind XSL-FO Converter.
Végezzünk néhány további kísérletet a programmal! Például legyen soronként 5 pixel magas, 100 deszkasor,
ennek megfelelően módosítsuk a kísérleti elrendezést (paraméterezzük a konstruktort), majd fordítsuk újra a
programot és futtassuk!
new GaltonDeszka(100, 5);
B.3. példa - A Galton deszka kísérlet programjának kiegészítései
Végezze el az Olvasó az alábbi továbbfejlesztéseket a programon!
• A kísérleti elrendezés ablakában jelenjen meg, hogy eddig hány golyó esett le a kísérlet során!
Ha csak ezzel az egy sorral bővítjük a paint() függvényt
Page 353
Számítási mellékletek
325 Created by XMLmind XSL-FO Converter.
g.drawString("golyók száma" + golyóSzámláló, 20, 100);
akkor az egymást követő kiírások, mivel nem törlődik a képernyő ezen része, a változó számok miatt
olvashatatlanok lesznek. Számos megoldás kínálkozik ennek elkerülésére, de nézzünk egy olyat, ami nem
használ fel olyan osztályokat az API-ból, amit még eddig nem használtunk. Az új kiírás előtt a háttérszínnel
újra kiírjuk a régi golyószámot.
StringBuffer golyókSb = new StringBuffer();
public void paint(java.awt.Graphics g) {
// Lemeszeljük az előző kiírást:
g.setColor(java.awt.Color.WHITE);
g.drawString(golyókSb.toString(), 20, 100);
// Összerakjuk az új kiírást:
golyókSb.delete(0, golyókSb.length());
golyókSb.append("golyók száma: ");
golyókSb.append(golyóSzámláló);
// és zölddel kiírjuk:
g.setColor(java.awt.Color.GREEN);
g.drawString(golyókSb.toString(), 20, 100);
// A deszkasorok és a golyó kirajzolása
...
• A kísérleti elrendezés ablakában jelenjen meg, hogy be van-e kapcsolva a pillanatfelvétel készítés.
• Az s billentyűt lenyomva a leeső golyó minden deszkasorbeli jobb vagy bal oldalra esési választásáról
készítsen a program egy pillanatfelvétel képet, majd maga kapcsolja ki e fotósorozat elkészítése után a
pillanatfelvétel készítést!
2.2. Mandelbrot halmaz programja
Az előző pont programját alakítjuk most át, a Mandelbrot halmazt fogjuk kiszámolni és kirajzolni. A
Mandelbrot halmaz, vagy a halmaz nagyításainak látható szépsége még azokat is megérinti, akik egyébként a
matematikai jellegű élmények befogadására nincsenek ráhangolva.
A Mandelbrot halmazt a [BARNSLEY KÖNYV] vagy ismeretterjesztő szinten a [CSÁSZÁR KÖNYV]
könyvekből ismerhetjük meg, de az algoritmust itt is ismertetjük, mert gyakorlásképpen be akarjuk programozni
Javaban! Az algoritmussal és az őt implementáló Java programmal párhuzamosan ismerkedünk meg, de az
alábbi tárgyalás után összefoglalásul a teljes program kódját is bevágjuk. Az algoritmus komplex számsíkon
működik. A komplex számsíkot benépesítő komplex számoknak könnyű geometriai interpretációt tulajdonítani,
amiután már barátságos velük dolgozni. Egy komplex számnak két része van, az egyik valós, a másik
imaginárius, azaz képzetesnek nevezett.
Page 354
Számítási mellékletek
326 Created by XMLmind XSL-FO Converter.
A Mandelbrot halmazt a komplex sík a, b, c, d határolta tartományára húzott
/** A komplex sík vizsgált tartománya [a,b]x[c,d]. */
protected double a, b, c, d;
/** A komplex sík vizsgált tartományára feszített
* háló szélessége és magassága. */
protected int szélesség, magasság;
szélesség széles és magasság magas rács pontjaiban számoljuk.
Ennek a rácsnak minden pontjában
// Végigzongorázzuk a szélesség x magasság hálót:
for(int j=0; j<magasság; ++j) {
sor = j;
for(int k=0; k<szélesség; ++k) {
Page 355
Számítási mellékletek
327 Created by XMLmind XSL-FO Converter.
elkezdjük számolni a zn+1 = zn2 + c iterációt a z0 = 0 és c a megfelelő rácspont kezdő értékekkel. A c komplex
szám reC + imC*i alakja alapján a rácspontnak megfelelő komplex szám valós része reC = a+k*dx, képzetes
része pedig imC = d-j*dy. Arra kell még figyelnünk, hogy a komplex számok szorzása rendhagyó, mert az
alábbi szabályt mindig szem előtt kell tartanunk i*i = -1, azaz a z2 = z*z = (reZ+imZ*i)*(reZ+imZ*i)= reZ*reZ
+ reZ*imZ*i + imZ*i*reZ + imZ*i*imZ*i = reZ*reZ - imZ*imZ + 2*reZ*imZ*i
// Végigzongorázzuk a szélesség x magasság hálót:
for(int j=0; j<magasság; ++j) {
sor = j;
for(int k=0; k<szélesség; ++k) {
// c = (reC, imC) a háló rácspontjainak
// megfelelő komplex szám
reC = a+k*dx;
imC = d-j*dy;
// z_0 = 0 = (reZ, imZ)
reZ = 0;
imZ = 0;
iteráció = 0;
// z_{n+1} = z_n * z_n + c iterációk
// számítása, amíg |z_n| < 2 vagy még
// nem értük el a 255 iterációt, ha
// viszont elértük, akkor úgy vesszük,
// hogy a kiinduláci c komplex számra
// az iteráció konvergens, azaz a c a
// Mandelbrot halmaz eleme
while(reZ*reZ + imZ*imZ < 4 && iteráció < iterációsHatár) {
// z_{n+1} = z_n * z_n + c
ujreZ = reZ*reZ - imZ*imZ + reC;
ujimZ = 2*reZ*imZ + imC;
reZ = ujreZ;
imZ = ujimZ;
++iteráció;
}
Az iterációk során azt figyeljük, hogy a zn mennyire távolodik el az induló z0 ponttól, azaz a koordinátarendszer
középpontjától, az origótól. Ha ez a távolság nagyobb lesz kettőnél, akkor azt mondjuk, hogy a vizsgált c
pontban az iteráció nem sűrűsödik be az origóhoz közeli valamelyik pontba. Ellenkező esetben, tehát ha az
iterációs határ elérése miatt lépünk ki a
while(reZ*reZ + imZ*imZ < 4 && iteráció < iterációsHatár) {
ciklusból, akkor azt tételezzük fel, hogy ebben a pontban az iteráció konvergens és a Mandelbrot halmaz
elemének tekintjük, feketére színezzük:
// sorozat konvergens, azaz iteráció = iterációsHatár
// ekkor az iteráció %= 256 egyenlő 255, mert az esetleges
// nagyítasok során az iteráció = valahány * 256 + 255
iteráció %= 256;
// így a halmaz elemeire 255-255 értéket használjuk,
// azaz (Red=0,Green=0,Blue=0) fekete színnel:
rgb = (255-iteráció)|
((255-iteráció) << 8) |
((255-iteráció) << 16);
// rajzoljuk a képre az éppen vizsgált pontot:
kép.setRGB(k, j, rgb);
Page 356
Számítási mellékletek
328 Created by XMLmind XSL-FO Converter.
A Mandelbrot halmazt kiszámoló, kirajzoló programunk teljes kódja a következő.
/*
* MandelbrotHalmaz.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* A Mandelbrot halmazt kiszámoló és kirajzoló osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
*/
public class MandelbrotHalmaz extends java.awt.Frame implements Runnable {
/** A komplex sík vizsgált tartománya [a,b]x[c,d]. */
protected double a, b, c, d;
/** A komplex sík vizsgált tartományára feszített
* háló szélessége és magassága. */
protected int szélesség, magasság;
/** A komplex sík vizsgált tartományára feszített hálónak megfelelő kép.*/
protected java.awt.image.BufferedImage kép;
/** Max. hány lépésig vizsgáljuk a z_{n+1} = z_n * z_n + c iterációt?
* (tk. most a nagyítási pontosság) */
protected int iterációsHatár = 255;
/** Jelzi, hogy éppen megy-e a szamítás? */
protected boolean számításFut = false;
/** Jelzi az ablakban, hogy éppen melyik sort számoljuk. */
protected int sor = 0;
/** A pillanatfelvételek számozásához. */
protected static int pillanatfelvételSzámláló = 0;
/**
* Létrehoz egy a Mandelbrot halmazt a komplex sík
* [a,b]x[c,d] tartománya felett kiszámoló
* <code>MandelbrotHalmaz</code> objektumot.
*
* @param a a [a,b]x[c,d] tartomány a koordinátája.
* @param b a [a,b]x[c,d] tartomány b koordinátája.
* @param c a [a,b]x[c,d] tartomány c koordinátája.
* @param d a [a,b]x[c,d] tartomány d koordinátája.
* @param szélesség a halmazt tartalmazó tömb szélessége.
* @param iterációsHatár a számítás pontossága.
*/
public MandelbrotHalmaz(double a, double b, double c, double d,
int szélesség, int iterációsHatár) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.szélesség = szélesség;
this.iterációsHatár = iterációsHatár;
// a magasság az (b-a) / (d-c) = szélesség / magasság
// arányból kiszámolva az alábbi lesz:
this.magasság = (int)(szélesség * ((d-c)/(b-a)));
// a kép, amire rárajzoljuk majd a halmazt
kép = new java.awt.image.BufferedImage(szélesség, magasság,
java.awt.image.BufferedImage.TYPE_INT_RGB);
// Az ablak bezárásakor kilépünk a programból.
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
setVisible(false);
System.exit(0);
}
});
// A billentyűzetről érkező események feldolgozása
addKeyListener(new java.awt.event.KeyAdapter() {
// Az 's', 'n' és 'm' gombok lenyomását figyeljük
public void keyPressed(java.awt.event.KeyEvent e) {
if(e.getKeyCode() == java.awt.event.KeyEvent.VK_S)
Page 357
Számítási mellékletek
329 Created by XMLmind XSL-FO Converter.
pillanatfelvétel();
// Az 'n' gomb benyomásával pontosabb számítást végzünk.
else if(e.getKeyCode() == java.awt.event.KeyEvent.VK_N) {
if(számításFut == false) {
MandelbrotHalmaz.this.iterációsHatár += 256;
// A számítás újra indul:
számításFut = true;
new Thread(MandelbrotHalmaz.this).start();
}
// Az 'm' gomb benyomásával pontosabb számítást végzünk,
// de közben sokkal magasabbra vesszük az iterációs
// határt, mint az 'n' használata esetén
} else if(e.getKeyCode() == java.awt.event.KeyEvent.VK_M) {
if(számításFut == false) {
MandelbrotHalmaz.this.iterációsHatár += 10*256;
// A számítás újra indul:
számításFut = true;
new Thread(MandelbrotHalmaz.this).start();
}
}
}
});
// Ablak tulajdonságai
setTitle("A Mandelbrot halmaz");
setResizable(false);
setSize(szélesség, magasság);
setVisible(true);
// A számítás indul:
számításFut = true;
new Thread(this).start();
}
/**
* A halmaz aktuális állapotának kirajzolása.
*/
public void paint(java.awt.Graphics g) {
// A Mandelbrot halmaz kirajzolása
g.drawImage(kép, 0, 0, this);
// Ha éppen fut a számítás, akkor egy vörös
// vonallal jelöljük, hogy melyik sorban tart:
if(számításFut) {
g.setColor(java.awt.Color.RED);
g.drawLine(0, sor, getWidth(), sor);
}
}
// Ne villogjon a felület (mert a "gyári" update()
// lemeszelné a vászon felületét).
public void update(java.awt.Graphics g) {
paint(g);
}
/**
* Pillanatfelvételek készítése.
*/
public void pillanatfelvétel() {
// Az elmentendő kép elkészítése:
java.awt.image.BufferedImage mentKép =
new java.awt.image.BufferedImage(szélesség, magasság,
java.awt.image.BufferedImage.TYPE_INT_RGB);
java.awt.Graphics g = mentKép.getGraphics();
g.drawImage(kép, 0, 0, this);
g.setColor(java.awt.Color.BLUE);
g.drawString("a=" + a, 10, 15);
g.drawString("b=" + b, 10, 30);
g.drawString("c=" + c, 10, 45);
g.drawString("d=" + d, 10, 60);
g.drawString("n=" + iterációsHatár, 10, 75);
g.dispose();
// A pillanatfelvétel képfájl nevének képzése:
StringBuffer sb = new StringBuffer();
sb = sb.delete(0, sb.length());
sb.append("MandelbrotHalmaz_");
sb.append(++pillanatfelvételSzámláló);
sb.append("_");
Page 358
Számítási mellékletek
330 Created by XMLmind XSL-FO Converter.
// A fájl nevébe belevesszük, hogy melyik tartományban
// találtuk a halmazt:
sb.append(a);
sb.append("_");
sb.append(b);
sb.append("_");
sb.append(c);
sb.append("_");
sb.append(d);
sb.append(".png");
// png formátumú képet mentünk
try {
javax.imageio.ImageIO.write(mentKép, "png",
new java.io.File(sb.toString()));
} catch(java.io.IOException e) {
e.printStackTrace();
}
}
/**
* A Mandelbrot halmaz számítási algoritmusa.
* Az algoritmus részletes ismertetését lásd például a
* [BARNSLEY KÖNYV] (M. Barnsley: Fractals everywhere,
* Academic Press, Boston, 1986) hivatkozásban vagy
* ismeretterjesztő szinten a [CSÁSZÁR KÖNYV] hivatkozásban.
*/
public void run() {
// A [a,b]x[c,d] tartományon milyen sűrű a
// megadott szélesség, magasság háló:
double dx = (b-a)/szélesség;
double dy = (d-c)/magasság;
double reC, imC, reZ, imZ, ujreZ, ujimZ;
int rgb;
// Hány iterációt csináltunk?
int iteráció = 0;
// Végigzongorázzuk a szélesség x magasság hálót:
for(int j=0; j<magasság; ++j) {
sor = j;
for(int k=0; k<szélesség; ++k) {
// c = (reC, imC) a háló rácspontjainak
// megfelelő komplex szám
reC = a+k*dx;
imC = d-j*dy;
// z_0 = 0 = (reZ, imZ)
reZ = 0;
imZ = 0;
iteráció = 0;
// z_{n+1} = z_n * z_n + c iterációk
// számítása, amíg |z_n| < 2 vagy még
// nem értük el a 255 iterációt, ha
// viszont elértük, akkor úgy vesszük,
// hogy a kiinduláci c komplex számra
// az iteráció konvergens, azaz a c a
// Mandelbrot halmaz eleme
while(reZ*reZ + imZ*imZ < 4 && iteráció < iterációsHatár) {
// z_{n+1} = z_n * z_n + c
ujreZ = reZ*reZ - imZ*imZ + reC;
ujimZ = 2*reZ*imZ + imC;
reZ = ujreZ;
imZ = ujimZ;
++iteráció;
}
// ha a < 4 feltétel nem teljesült és a
// iteráció < iterációsHatár sérülésével lépett ki, azaz
// feltesszük a c-ről, hogy itt a z_{n+1} = z_n * z_n + c
// sorozat konvergens, azaz iteráció = iterációsHatár
// ekkor az iteráció %= 256 egyenlő 255, mert az esetleges
// nagyítasok során az iteráció = valahány * 256 + 255
iteráció %= 256;
// így a halmaz elemeire 255-255 értéket használjuk,
// azaz (Red=0,Green=0,Blue=0) fekete színnel:
Page 359
Számítási mellékletek
331 Created by XMLmind XSL-FO Converter.
rgb = (255-iteráció)|
((255-iteráció) << 8) |
((255-iteráció) << 16);
// rajzoljuk a képre az éppen vizsgált pontot:
kép.setRGB(k, j, rgb);
}
repaint();
}
számításFut = false;
}
/**
* Példányosít egy Mandelbrot halmazt kiszámoló obektumot.
*/
public static void main(String[] args) {
// A halmazt a komplex sík [-2.0, .7]x[-1.35, 1.35] tartományában
// keressük egy 400x400-as hálóval:
new MandelbrotHalmaz(-2.0, .7, -1.35, 1.35, 600, 255);
}
}
Könnyen végezhetünk saját számításokat az osztály imént megadott kódjával. Jelöljük ki a forrásszöveget és
illesszük be egy MandelbrotHalmaz.java nevű állományba, majd fordítsuk a állományt és futtassuk a
számítást:
C:\...> javac MandelbrotHalmaz.java
C:\...> java MandelbrotHalmaz
A program futása alatt az s billentyűt lenyomva a kiszámolt halmazról egy felvételt készít a program. A készített
képeket abban a könyvtárban találjuk, ahonnan a programot a fent ajánlott módon elindítottuk. Ilyen kép például
az alábbi MandelbrotHalmaz_1_-2.0_0.7_-1.35_1.35.png kép.
Page 360
Számítási mellékletek
332 Created by XMLmind XSL-FO Converter.
A program alább bemutatott ablakában a kép adatait nem írattuk ki, illetve a kép tetejéből az ablak fejléce
néhány pixelsort eltakar.
Page 361
Számítási mellékletek
333 Created by XMLmind XSL-FO Converter.
2.3. Mandelbrot halmaz nagyító programja
Az előző pont programját fejlesztjük most tovább, a program által kiszámolt és kirajzolt Mandelbrot halmaz
valamely részét fogjuk kinagyítani és annak valamely részét ... és így tovább. Az áhított nagyító programot úgy
valósítjuk meg, hogy a fejlesztendő MandelbrotHalmazNagyító osztállyal kiterjesztjük a MandelbrotHalmaz
osztályunkat.
/*
* MandelbrotHalmazNagyító.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* A Mandelbrot halmazt nagyító és kirajzoló osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
*/
public class MandelbrotHalmazNagyító extends MandelbrotHalmaz {
/** A nagyítandó kijelölt területet bal felső sarka. */
private int x, y;
/** A nagyítandó kijelölt terület szélessége és magassága. */
private int mx, my;
/**
Page 362
Számítási mellékletek
334 Created by XMLmind XSL-FO Converter.
* Létrehoz egy a Mandelbrot halmazt a komplex sík
* [a,b]x[c,d] tartománya felett kiszámoló és nygítani tudó
* <code>MandelbrotHalmazNagyító</code> objektumot.
*
* @param a a [a,b]x[c,d] tartomány a koordinátája.
* @param b a [a,b]x[c,d] tartomány b koordinátája.
* @param c a [a,b]x[c,d] tartomány c koordinátája.
* @param d a [a,b]x[c,d] tartomány d koordinátája.
* @param szélesség a halmazt tartalmazó tömb szélessége.
* @param iterációsHatár a számítás pontossága.
*/
public MandelbrotHalmazNagyító(double a, double b, double c, double d,
int szélesség, int iterációsHatár) {
// Az ős osztály konstruktorának hívása
super(a, b, c, d, szélesség, iterációsHatár);
setTitle("A Mandelbrot halmaz nagyításai");
// Egér kattintó események feldolgozása:
addMouseListener(new java.awt.event.MouseAdapter() {
// Egér kattintással jelöljük ki a nagyítandó területet
// bal felső sarkát:
public void mousePressed(java.awt.event.MouseEvent m) {
// A nagyítandó kijelölt területet bal felső sarka:
x = m.getX();
y = m.getY();
mx = 0;
my = 0;
repaint();
}
// Vonszolva kijelölünk egy területet...
// Ha felengedjük, akkor a kijelölt terület
// újraszámítása indul:
public void mouseReleased(java.awt.event.MouseEvent m) {
double dx = (MandelbrotHalmazNagyító.this.b
- MandelbrotHalmazNagyító.this.a)
/MandelbrotHalmazNagyító.this.szélesség;
double dy = (MandelbrotHalmazNagyító.this.d
- MandelbrotHalmazNagyító.this.c)
/MandelbrotHalmazNagyító.this.magasság;
// Az új Mandelbrot nagyító objektum elkészítése:
new MandelbrotHalmazNagyító(MandelbrotHalmazNagyító.this.a+x*dx,
MandelbrotHalmazNagyító.this.a+x*dx+mx*dx,
MandelbrotHalmazNagyító.this.d-y*dy-my*dy,
MandelbrotHalmazNagyító.this.d-y*dy,
600,
MandelbrotHalmazNagyító.this.iterációsHatár);
}
});
// Egér mozgás események feldolgozása:
addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
// Vonszolással jelöljük ki a négyzetet:
public void mouseDragged(java.awt.event.MouseEvent m) {
// A nagyítandó kijelölt terület szélessége és magassága:
mx = m.getX() - x;
my = m.getY() - y;
repaint();
}
});
}
/**
* Pillanatfelvételek készítése.
*/
public void pillanatfelvétel() {
// Az elmentendő kép elkészítése:
java.awt.image.BufferedImage mentKép =
new java.awt.image.BufferedImage(szélesség, magasság,
java.awt.image.BufferedImage.TYPE_INT_RGB);
java.awt.Graphics g = mentKép.getGraphics();
g.drawImage(kép, 0, 0, this);
g.setColor(java.awt.Color.BLUE);
g.drawString("a=" + a, 10, 15);
g.drawString("b=" + b, 10, 30);
g.drawString("c=" + c, 10, 45);
Page 363
Számítási mellékletek
335 Created by XMLmind XSL-FO Converter.
g.drawString("d=" + d, 10, 60);
g.drawString("n=" + iterációsHatár, 10, 75);
if(számításFut) {
g.setColor(java.awt.Color.RED);
g.drawLine(0, sor, getWidth(), sor);
}
g.setColor(java.awt.Color.GREEN);
g.drawRect(x, y, mx, my);
g.dispose();
// A pillanatfelvétel képfájl nevének képzése:
StringBuffer sb = new StringBuffer();
sb = sb.delete(0, sb.length());
sb.append("MandelbrotHalmazNagyitas_");
sb.append(++pillanatfelvételSzámláló);
sb.append("_");
// A fájl nevébe belevesszük, hogy melyik tartományban
// találtuk a halmazt:
sb.append(a);
sb.append("_");
sb.append(b);
sb.append("_");
sb.append(c);
sb.append("_");
sb.append(d);
sb.append(".png");
// png formátumú képet mentünk
try {
javax.imageio.ImageIO.write(mentKép, "png",
new java.io.File(sb.toString()));
} catch(java.io.IOException e) {
e.printStackTrace();
}
}
/**
* A nagyítandó kijelölt területet jelző négyzet kirajzolása.
*/
public void paint(java.awt.Graphics g) {
// A Mandelbrot halmaz kirajzolása
g.drawImage(kép, 0, 0, this);
// Ha éppen fut a számítás, akkor egy vörös
// vonallal jelöljük, hogy melyik sorban tart:
if(számításFut) {
g.setColor(java.awt.Color.RED);
g.drawLine(0, sor, getWidth(), sor);
}
// A jelző négyzet kirajzolása:
g.setColor(java.awt.Color.GREEN);
g.drawRect(x, y, mx, my);
}
/**
* Példányosít egy Mandelbrot halmazt nagyító obektumot.
*/
public static void main(String[] args) {
// A kiinduló halmazt a komplex sík [-2.0, .7]x[-1.35, 1.35]
// tartományában keressük egy 600x600-as hálóval és az
// aktuális nagyítási pontossággal:
new MandelbrotHalmazNagyító(-2.0, .7, -1.35, 1.35, 600, 255);
}
}
Ezzel a továbbfejlesztéssel ugyancsak könnyen végezhetünk saját nagyításokat az osztály imént megadott
kódjával. Jelöljük ki a forrásszöveget és illesszük be egy MandelbrotHalmazNagyító.java nevű állományba -
amit ugyanabba a könyvtárba helyezünk, mint az előző MandelbrotHalmaz.java állományt - majd fordítsuk a
állományt és futtassuk a számítást:
C:\...> javac MandelbrotHalmazNagyító.java
C:\...> java MandelbrotHalmazNagyító
Page 364
Számítási mellékletek
336 Created by XMLmind XSL-FO Converter.
Nézzünk meg néhány, a program futása alatt az s billentyű lenyomásával készített felvételt a nagyított
Mandelbrot halmazokról:
A bal egérgomb lenyomásával kijelöljük a nagyítandó területet. A MandelbrotHalmazNagyító osztályban ezt
egy beágyazott, a MouseAdapter adapter osztályt kiterjesztő névtelen osztálybeli eseménykezelő objektummal
oldjuk meg:
// Egér kattintó események feldolgozása:
addMouseListener(new java.awt.event.MouseAdapter() {
// Egér kattintással jelöljük ki a nagyítandó területet
// bal felső sarkát:
public void mousePressed(java.awt.event.MouseEvent m) {
// A nagyítandó kijelölt területet bal felső sarka:
x = m.getX();
y = m.getY();
mx = 0;
my = 0;
repaint();
}
A kijelölő, zölddel kirajzolt, nagyítandó területet kijelölő téglalap bal felső sarkának koordinátái az osztály x és
y tagjai. A kijelölő téglalap szélessége és magassága az mx és az my tagok.
/** A nagyítandó kijelölt területet bal felső sarka. */
private int x, y;
/** A nagyítandó kijelölt terület szélessége és magassága. */
private int mx, my;
Ezek állítása történik az eseménykezelő egérkattintást feldolgozó mousePressed() módszerében. A függvény
aktuális paramétereként megkapott eseményobjektumtól megkérdezzük a kattintás helyét:
x = m.getX();
y = m.getY();
mx = 0;
my = 0;
repaint();
}
Page 365
Számítási mellékletek
337 Created by XMLmind XSL-FO Converter.
az mx és my szélességet, magasságot nullára állítjuk, ezeket majd az egérgomb nyomvatartása melletti vonszolás
állítja be a vonszolás méretének megfelelő értékre. Az egérmutató vonszolásának megfigyelését ugyancsak egy
beágyazott, de most a MouseMotionAdapter adapter osztályt kiterjesztő névtelen osztálybeli eseménykezelő
objektummal oldjuk meg:
// Egér mozgás események feldolgozása:
addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
// Vonszolással jelöljük ki a négyzetet:
public void mouseDragged(java.awt.event.MouseEvent m) {
// A nagyítandó kijelölt terület szélessége és magassága:
mx = m.getX() - x;
my = m.getY() - y;
repaint();
}
});
Az x és y példányok tárolják a négyzet bal felső sarkának oszlop és sor koordinátáját, ezeket az egérmutató
aktuális helyéből kivonva kapjuk a kijelölt terület szélességét, illetve magasságát, például: mx = m.getX() -
x;. A repaint() hívása biztosítja, hogy a megváltozott példánytagoknak megfelelő zöld tégla kirajzolásra
kerüljön, azaz a paint() függvény meghívódjon, ahol
// A jelző négyzet kirajzolása:
g.setColor(java.awt.Color.GREEN);
g.drawRect(x, y, mx, my);
Page 366
Számítási mellékletek
338 Created by XMLmind XSL-FO Converter.
Page 367
Számítási mellékletek
339 Created by XMLmind XSL-FO Converter.
Ha úgy érezzük, hogy a nagyítások során leromlott a pontosság, akkor az n gomb nyomásával növelni tudjuk a
számítások iterációs határát. Ezt a billenytűzet eseményt egy beágyazott, a KeyAdapter adapter osztályt
kiterjesztő névtelen osztálybeli eseménykezelő objektummal oldjuk meg:
// A billentyűzetről érkező események feldolgozása
addKeyListener(new java.awt.event.KeyAdapter() {
// Az 's', 'n' és 'm' gombok lenyomását figyeljük
public void keyPressed(java.awt.event.KeyEvent e) {
...
// Az 'n' gomb benyomásával pontosabb számítást végzünk.
else if(e.getKeyCode() == java.awt.event.KeyEvent.VK_N) {
if(számításFut == false) {
MandelbrotHalmaz.this.iterációsHatár += 256;
Page 368
Számítási mellékletek
340 Created by XMLmind XSL-FO Converter.
// A számítás újra indul:
számításFut = true;
new Thread(MandelbrotHalmaz.this).start();
}
Ha éppen nem fut számítás, akkor növeljük a számítások iterációsHatár iterációs határát, majd készítünk egy
számítást végző new Thread(MandelbrotHalmaz.this).start(); szálat. Ez a pontosabb számítás ekkor jól
megfigyelhető, de egy vörös csíkkal is jeleztük
// Ha éppen fut a számítás, akkor egy vörös
// vonallal jelöljük, hogy melyik sorban tart:
if(számításFut) {
g.setColor(java.awt.Color.RED);
g.drawLine(0, sor, getWidth(), sor);
}
a rajzolást végző paint() függvényekben.
Page 369
Számítási mellékletek
341 Created by XMLmind XSL-FO Converter.
Page 370
Számítási mellékletek
342 Created by XMLmind XSL-FO Converter.
Page 371
Számítási mellékletek
343 Created by XMLmind XSL-FO Converter.
Page 372
Számítási mellékletek
344 Created by XMLmind XSL-FO Converter.
Page 373
Számítási mellékletek
345 Created by XMLmind XSL-FO Converter.
Page 374
Számítási mellékletek
346 Created by XMLmind XSL-FO Converter.
B.4. példa - A Mandelbrot halmaz nagyító programjának kiegészítései
Végezze el az Olvasó az alábbi továbbfejlesztéseket a programon! Próbálkozzunk például mindenféle más
színezési stratégiákkal!
• Az alábbi módosítás pontosabb, de kevésbé látványos színezést produkál:
iteráció = iteráció / (iterációsHatár / 255);
rgb = (255-iteráció)|
(255-iteráció) << 8 |
((255-iteráció) << 16);
• Ezzel a színezéssel már a vörös árnyalataiban pompáznak a nagyítások:
if(iteráció == iterációsHatár)
rgb = 0;
else {
iteráció = iteráció % 255;
rgb = 0|
0 << 8 |
((255-iteráció) << 16);
}
Page 375
Számítási mellékletek
347 Created by XMLmind XSL-FO Converter.
• Nemcsak egyfajta szín árnyalatait használva:
if(iteráció == iterációsHatár)
rgb = 0;
else {
iteráció = iteráció % 255;
rgb = (255-iteráció%16)|
(255-iteráció%64) << 8 |
((255-iteráció) << 16);
}
Page 376
Számítási mellékletek
348 Created by XMLmind XSL-FO Converter.
2.4. Mandelbrot halmaz pontjait grafikusan iteráló program
Az előző pontban kifejlesztett nagyító program már jól használható matematikai kísérleti eszközünk volt, erről
tanúskodtott az imént bemutatott nagyítási fotósorozat is. Ebben a részben a nagyítóhoz egy olyan számoló szál
objektumot készítünk, ami képes a felhasználó kérésére a halmaz egy tetszőleges pontjából megvizsgálni a zn+1 =
zn2 + c iteráció lépéseit: kirajzoljuk a komplex síkra az iteráció bejárta z0, z1, z3 ... komplex számokat és az
egymást követőeket egy egyenessel kötjük majd össze:
g.drawLine(
(int)((reZ - a)/dx),
(int)((d - imZ)/dy),
(int)((ujreZ - a)/dx),
(int)((d - ujimZ)/dy)
);
A program felhasználója a bal egér gombbal jelöli ki azt a pontot, amelyből az iterációban szereplő pontokat
meg akarja tekinteni. A jobb egér gomb nyomása továbbra is a nagyítandó terület bal felső sarkának
koordinátája.
// Egér kattintó események feldolgozása:
addMouseListener(new java.awt.event.MouseAdapter() {
// Egér kattintással jelöljük ki a nagyítandó területet
// bal felső sarkát vagy ugyancsak egér kattintással
// vizsgáljuk egy adott pont iterációit:
public void mousePressed(java.awt.event.MouseEvent m) {
Page 377
Számítási mellékletek
349 Created by XMLmind XSL-FO Converter.
// Az egérmutató pozíciója
x = m.getX();
y = m.getY();
// Az 1. egér gombbal a nagyítandó terület kijelölését
// végezzük:
if(m.getButton() == java.awt.event.MouseEvent.BUTTON1 ) {
// A nagyítandó kijelölt területet bal felső sarka: (x,y)
// és szélessége (majd a vonszolás növeli)
mx = 0;
my = 0;
repaint();
} else {
// Nem az 1. egér gombbal az egérmutató mutatta c
// komplex számból indított iterációkat vizsgálhatjuk
MandelbrotIterációk iterációk =
new MandelbrotIterációk(
MandelbrotHalmazNagyító.this, 50);
new Thread(iterációk).start();
}
}
Most fejlesztendő szál osztályunkat a MandelbrotIterációk osztályban implementáljuk:
public class MandelbrotIterációk implements Runnable{
Az osztály hozzáfér Mandelbrot halmazunk adataihoz, párhuzamosan végrehajtandó run() metódusa is
majdnem ugyanaz, mint a MandelbrotHalmaz osztályé, de itt nem az egész rácson kell az iterációt vizsgálnunk,
hanem csupán a MandelbrotHalmazNagyító futó objektumon az egérmutató mutatta rácspontban.
/** Az vizsgált pontból induló iterációk bemutatása. */
public void run() {
/* Az alábbi kód javarészt a MandelbrotHalmaz.java számolást
végző run() módszeréből származik, hiszen ugyanazt csináljuk,
csak most nem a hálón megyünk végig, hanem a háló adott a
példányosításunkkor az egérmutató mutatta csomópontjában (ennek
felel meg a c kompelx szám) számolunk, tehát a két külső for
ciklus nem kell. */
// A [a,b]x[c,d] tartományon milyen sűrű a
// megadott szélesség, magasság háló:
double dx = (b-a)/szélesség;
double dy = (d-c)/magasság;
double reC, imC, reZ, imZ, ujreZ, ujimZ;
// Hány iterációt csináltunk?
int iteráció = 0;
// c = (reC, imC) a háló rácspontjainak
// megfelelő komplex szám
reC = a+k*dx;
imC = d-j*dy;
// z_0 = 0 = (reZ, imZ)
reZ = 0;
imZ = 0;
iteráció = 0;
// z_{n+1} = z_n * z_n + c iterációk
// számítása, amíg |z_n| < 2 vagy még
// nem értük el a 255 iterációt, ha
// viszont elértük, akkor úgy vesszük,
// hogy a kiinduláci c komplex számra
// az iteráció konvergens, azaz a c a
// Mandelbrot halmaz eleme
while(reZ*reZ + imZ*imZ < 4 && iteráció < 255) {
// z_{n+1} = z_n * z_n + c
ujreZ = reZ*reZ - imZ*imZ + reC;
ujimZ = 2*reZ*imZ + imC;
Page 378
Számítási mellékletek
350 Created by XMLmind XSL-FO Converter.
// az iteráció (reZ, imZ) -> (ujreZ, ujimZ)
// ezt az egyenest kell kirajzolnunk, de most
// a komplex számokat vissza kell transzformálnunk
// a rács oszlop, sor koordinátájává:
java.awt.Graphics g = kép.getGraphics();
g.setColor(java.awt.Color.WHITE);
g.drawLine(
(int)((reZ - a)/dx),
(int)((d - imZ)/dy),
(int)((ujreZ - a)/dx),
(int)((d - ujimZ)/dy)
);
g.dispose();
nagyító.repaint();
reZ = ujreZ;
imZ = ujimZ;
++iteráció;
// Várakozunk, hogy közben csodálhassuk az iteráció
// látványát:
try {
Thread.sleep(várakozás);
} catch (InterruptedException e) {}
}
}
Page 379
Számítási mellékletek
351 Created by XMLmind XSL-FO Converter.
A következő ábrán egy olyan futó MandelbrotHalmazNagyító objektumot látunk, ahol néhány pontban
megnéztük az iterációkban szereplő pontokat.
2.5. A Mandelbrot halmazzal kapcsolatos osztályaink összefoglalása
A Mandelbrot halmazos programozási gyakorlatunkhoz három osztályt fejlesztettünk ki, a MandelbrotHalmaz,
a MandelbrotHalmazNagyító és a MandelbrotIterációk osztályokat. Az osztályokkal való kísérletezéshez
az alább közölt megfelelő MandelbrotHalmaz.java, a MandelbrotHalmazNagyító.java és a
MandelbrotIterációk.java forrásállományokat helyezzük egy könyvtárba, majd egy olyan parancsablakból,
ahol a Java megfelelően be van állítva, fordítsuk az osztályokat és futtassuk a programot így:
C:\...> javac MandelbrotHalmazNagyító.java
C:\...> java MandelbrotHalmazNagyító
Korábbi tárgyalásunknak megfelelően a program ablakok a következő inputokat dolgozzák fel:
• Az s billentyű lenyomásával egy felvétel kép készül az aktív ablakban számolt fraktálról.
• Az n billentyű lenyomásával növeljük a halmaz kiszámolásának pontosságát, 256-al megnöveljük az iterációk
számát.
Page 380
Számítási mellékletek
352 Created by XMLmind XSL-FO Converter.
• Az m billentyű lenyomásával nagyobb ugrással növeljük a halmaz kiszámolásának pontosságát, 10*256-al
megnöveljük az iterációk számát.
• Az egérmutató bal gombjával kijelöljük a nagyítandó terület bal felső sarkát, az egér vonszolásával megadjuk
a terület nagyságát, az egérgomb felengedésére új ablakban megkezdődik a kijelölt terület nagyítása.
• Az egérmutató jobb gombjával kijelölt pontból végzett iterációkat grafikusan kirajzolja a program.
A következő ábrán mutatjuk be kifejlesztett programunk tipikus használati esetét: számos ablakban
nagyítgatunk, visszatérünk egy korábbihoz, pontosítjuk a számítást stb.
2.5.1. A MandelbrotHalmaz osztály
A korábbi pontok fejlesztései eredményeképpen a MandelbrotHalmaz osztály alábbi, a 0.0.2 verziója az
aktuális. Az alábbi forrásszöveget kijelölve a MandelbrotHalmaz.java forrásállományba kell beillesztenie az
Olvasónak a felhasználáshoz.
/*
* MandelbrotHalmaz.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* A Mandelbrot halmazt kiszámoló és kirajzoló osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.2
Page 381
Számítási mellékletek
353 Created by XMLmind XSL-FO Converter.
*/
public class MandelbrotHalmaz extends java.awt.Frame implements Runnable {
/** A komplex sík vizsgált tartománya [a,b]x[c,d]. */
protected double a, b, c, d;
/** A komplex sík vizsgált tartományára feszített
* háló szélessége és magassága. */
protected int szélesség, magasság;
/** A komplex sík vizsgált tartományára feszített hálónak megfelelő kép.*/
protected java.awt.image.BufferedImage kép;
/** Max. hány lépésig vizsgáljuk a z_{n+1} = z_n * z_n + c iterációt?
* (tk. most a nagyítási pontosság) */
protected int iterációsHatár = 255;
/** Jelzi, hogy éppen megy-e a szamítás? */
protected boolean számításFut = false;
/** Jelzi az ablakban, hogy éppen melyik sort számoljuk. */
protected int sor = 0;
/** A pillanatfelvételek számozásához. */
protected static int pillanatfelvételSzámláló = 0;
/**
* Létrehoz egy a Mandelbrot halmazt a komplex sík
* [a,b]x[c,d] tartománya felett kiszámoló
* <code>MandelbrotHalmaz</code> objektumot.
*
* @param a a [a,b]x[c,d] tartomány a koordinátája.
* @param b a [a,b]x[c,d] tartomány b koordinátája.
* @param c a [a,b]x[c,d] tartomány c koordinátája.
* @param d a [a,b]x[c,d] tartomány d koordinátája.
* @param szélesség a halmazt tartalmazó tömb szélessége.
* @param iterációsHatár a számítás pontossága.
*/
public MandelbrotHalmaz(double a, double b, double c, double d,
int szélesség, int iterációsHatár) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.szélesség = szélesség;
this.iterációsHatár = iterációsHatár;
// a magasság az (b-a) / (d-c) = szélesség / magasság
// arányból kiszámolva az alábbi lesz:
this.magasság = (int)(szélesség * ((d-c)/(b-a)));
// a kép, amire rárajzoljuk majd a halmazt
kép = new java.awt.image.BufferedImage(szélesség, magasság,
java.awt.image.BufferedImage.TYPE_INT_RGB);
// Az ablak bezárásakor kilépünk a programból.
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
setVisible(false);
System.exit(0);
}
});
// A billentyűzetről érkező események feldolgozása
addKeyListener(new java.awt.event.KeyAdapter() {
// Az 's', 'n' és 'm' gombok lenyomását figyeljük
public void keyPressed(java.awt.event.KeyEvent e) {
if(e.getKeyCode() == java.awt.event.KeyEvent.VK_S)
pillanatfelvétel();
// Az 'n' gomb benyomásával pontosabb számítást végzünk.
else if(e.getKeyCode() == java.awt.event.KeyEvent.VK_N) {
if(számításFut == false) {
MandelbrotHalmaz.this.iterációsHatár += 256;
// A számítás újra indul:
számításFut = true;
new Thread(MandelbrotHalmaz.this).start();
}
// Az 'm' gomb benyomásával pontosabb számítást végzünk,
// de közben sokkal magasabbra vesszük az iterációs
// határt, mint az 'n' használata esetén
} else if(e.getKeyCode() == java.awt.event.KeyEvent.VK_M) {
if(számításFut == false) {
MandelbrotHalmaz.this.iterációsHatár += 10*256;
// A számítás újra indul:
Page 382
Számítási mellékletek
354 Created by XMLmind XSL-FO Converter.
számításFut = true;
new Thread(MandelbrotHalmaz.this).start();
}
}
}
});
// Ablak tulajdonságai
setTitle("A Mandelbrot halmaz");
setResizable(false);
setSize(szélesség, magasság);
setVisible(true);
// A számítás indul:
számításFut = true;
new Thread(this).start();
}
/** A halmaz aktuális állapotának kirajzolása. */
public void paint(java.awt.Graphics g) {
// A Mandelbrot halmaz kirajzolása
g.drawImage(kép, 0, 0, this);
// Ha éppen fut a számítás, akkor egy vörös
// vonallal jelöljük, hogy melyik sorban tart:
if(számításFut) {
g.setColor(java.awt.Color.RED);
g.drawLine(0, sor, getWidth(), sor);
}
}
// Ne villogjon a felület (mert a "gyári" update()
// lemeszelné a vászon felületét).
public void update(java.awt.Graphics g) {
paint(g);
}
/** Pillanatfelvételek készítése. */
public void pillanatfelvétel() {
// Az elmentendő kép elkészítése:
java.awt.image.BufferedImage mentKép =
new java.awt.image.BufferedImage(szélesség, magasság,
java.awt.image.BufferedImage.TYPE_INT_RGB);
java.awt.Graphics g = mentKép.getGraphics();
g.drawImage(kép, 0, 0, this);
g.setColor(java.awt.Color.BLUE);
g.drawString("a=" + a, 10, 15);
g.drawString("b=" + b, 10, 30);
g.drawString("c=" + c, 10, 45);
g.drawString("d=" + d, 10, 60);
g.drawString("n=" + iterációsHatár, 10, 75);
g.dispose();
// A pillanatfelvétel képfájl nevének képzése:
StringBuffer sb = new StringBuffer();
sb = sb.delete(0, sb.length());
sb.append("MandelbrotHalmaz_");
sb.append(++pillanatfelvételSzámláló);
sb.append("_");
// A fájl nevébe belevesszük, hogy melyik tartományban
// találtuk a halmazt:
sb.append(a);
sb.append("_");
sb.append(b);
sb.append("_");
sb.append(c);
sb.append("_");
sb.append(d);
sb.append(".png");
// png formátumú képet mentünk
try {
javax.imageio.ImageIO.write(mentKép, "png",
new java.io.File(sb.toString()));
} catch(java.io.IOException e) {
e.printStackTrace();
}
}
/**
* A Mandelbrot halmaz számítási algoritmusa.
Page 383
Számítási mellékletek
355 Created by XMLmind XSL-FO Converter.
* Az algoritmus részletes ismertetését lásd például a
* [BARNSLEY KÖNYV] (M. Barnsley: Fractals everywhere,
* Academic Press, Boston, 1986) hivatkozásban vagy
* ismeretterjesztő szinten a [CSÁSZÁR KÖNYV] hivatkozásban.
*/
public void run() {
// A [a,b]x[c,d] tartományon milyen sűrű a
// megadott szélesség, magasság háló:
double dx = (b-a)/szélesség;
double dy = (d-c)/magasság;
double reC, imC, reZ, imZ, ujreZ, ujimZ;
int rgb;
// Hány iterációt csináltunk?
int iteráció = 0;
// Végigzongorázzuk a szélesség x magasság hálót:
for(int j=0; j<magasság; ++j) {
sor = j;
for(int k=0; k<szélesség; ++k) {
// c = (reC, imC) a háló rácspontjainak
// megfelelő komplex szám
reC = a+k*dx;
imC = d-j*dy;
// z_0 = 0 = (reZ, imZ)
reZ = 0;
imZ = 0;
iteráció = 0;
// z_{n+1} = z_n * z_n + c iterációk
// számítása, amíg |z_n| < 2 vagy még
// nem értük el a 255 iterációt, ha
// viszont elértük, akkor úgy vesszük,
// hogy a kiinduláci c komplex számra
// az iteráció konvergens, azaz a c a
// Mandelbrot halmaz eleme
while(reZ*reZ + imZ*imZ < 4 && iteráció < iterációsHatár) {
// z_{n+1} = z_n * z_n + c
ujreZ = reZ*reZ - imZ*imZ + reC;
ujimZ = 2*reZ*imZ + imC;
reZ = ujreZ;
imZ = ujimZ;
++iteráció;
}
// ha a < 4 feltétel nem teljesült és a
// iteráció < iterációsHatár sérülésével lépett ki, azaz
// feltesszük a c-ről, hogy itt a z_{n+1} = z_n * z_n + c
// sorozat konvergens, azaz iteráció = iterációsHatár
// ekkor az iteráció %= 256 egyenlő 255, mert az esetleges
// nagyítasok során az iteráció = valahány * 256 + 255
iteráció %= 256;
// így a halmaz elemeire 255-255 értéket használjuk,
// azaz (Red=0,Green=0,Blue=0) fekete színnel:
rgb = (255-iteráció)|
((255-iteráció) << 8) |
((255-iteráció) << 16);
// rajzoljuk a képre az éppen vizsgált pontot:
kép.setRGB(k, j, rgb);
}
repaint();
}
számításFut = false;
}
/** Az aktuális Mandelbrot halmaz [a,b]x[c,d] adatai.
* @return double a */
public double getA() {
return a;
}
/** Az aktuális Mandelbrot halmaz [a,b]x[c,d] adatai.
* @return double b */
public double getB() {
return b;
}
Page 384
Számítási mellékletek
356 Created by XMLmind XSL-FO Converter.
/** Az aktuális Mandelbrot halmaz [a,b]x[c,d] adatai.
* @return double c */
public double getC() {
return c;
}
/** Az aktuális Mandelbrot halmaz [a,b]x[c,d] adatai.
* @return double d */
public double getD() {
return d;
}
/** Az aktuális Mandelbrot halmaz feletti rács adatai.
* @return int szélesség */
public int getSz() {
return szélesség;
}
/** Az aktuális Mandelbrot halmaz feletti rács adatai.
* @return int magasság */
public int getM() {
return magasság;
}
/** Az aktuális Mandelbrot halmazt tartalmazó kép.
* @return BufferedImage kép */
public java.awt.image.BufferedImage kép() {
return kép;
}
/** Példányosít egy Mandelbrot halmazt kiszámoló obektumot. */
public static void main(String[] args) {
// A halmazt a komplex sík [-2.0, .7]x[-1.35, 1.35] tartományában
// keressük egy 400x400-as hálóval:
new MandelbrotHalmaz(-2.0, .7, -1.35, 1.35, 600, 255);
}
}
2.5.2. A MandelbrotHalmazNagyító osztály
A korábbi pontok fejlesztései eredményeképpen a MandelbrotHalmazNagyító osztály alábbi, a 0.0.2 verziója
az aktuális. Az alábbi forrásszöveget kijelölve a MandelbrotHalmazNagyító.java forrásállományba kell
beillesztenie az Olvasónak a felhasználáshoz.
/*
* MandelbrotHalmazNagyító.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* A Mandelbrot halmazt nagyító és kirajzoló osztály.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.2
*/
public class MandelbrotHalmazNagyító extends MandelbrotHalmaz {
/** A nagyítandó kijelölt területet bal felső sarka. */
private int x, y;
/** A nagyítandó kijelölt terület szélessége és magassága. */
private int mx, my;
/**
* Létrehoz egy a Mandelbrot halmazt a komplex sík
* [a,b]x[c,d] tartománya felett kiszámoló és nygítani tudó
* <code>MandelbrotHalmazNagyító</code> objektumot.
*
* @param a a [a,b]x[c,d] tartomány a koordinátája.
* @param b a [a,b]x[c,d] tartomány b koordinátája.
* @param c a [a,b]x[c,d] tartomány c koordinátája.
* @param d a [a,b]x[c,d] tartomány d koordinátája.
* @param szélesség a halmazt tartalmazó tömb szélessége.
Page 385
Számítási mellékletek
357 Created by XMLmind XSL-FO Converter.
* @param iterációsHatár a számítás pontossága.
*/
public MandelbrotHalmazNagyító(double a, double b, double c, double d,
int szélesség, int iterációsHatár) {
// Az ős osztály konstruktorának hívása
super(a, b, c, d, szélesség, iterációsHatár);
setTitle("A Mandelbrot halmaz nagyításai");
// Egér kattintó események feldolgozása:
addMouseListener(new java.awt.event.MouseAdapter() {
// Egér kattintással jelöljük ki a nagyítandó területet
// bal felső sarkát vagy ugyancsak egér kattintással
// vizsgáljuk egy adott pont iterációit:
public void mousePressed(java.awt.event.MouseEvent m) {
// Az egérmutató pozíciója
x = m.getX();
y = m.getY();
// Az 1. egér gombbal a nagyítandó terület kijelölését
// végezzük:
if(m.getButton() == java.awt.event.MouseEvent.BUTTON1 ) {
// A nagyítandó kijelölt területet bal felső sarka: (x,y)
// és szélessége (majd a vonszolás növeli)
mx = 0;
my = 0;
repaint();
} else {
// Nem az 1. egér gombbal az egérmutató mutatta c
// komplex számból indított iterációkat vizsgálhatjuk
MandelbrotIterációk iterációk =
new MandelbrotIterációk(
MandelbrotHalmazNagyító.this, 50);
new Thread(iterációk).start();
}
}
// Vonszolva kijelölünk egy területet...
// Ha felengedjük, akkor a kijelölt terület
// újraszámítása indul:
public void mouseReleased(java.awt.event.MouseEvent m) {
if(m.getButton() == java.awt.event.MouseEvent.BUTTON1 ) {
double dx = (MandelbrotHalmazNagyító.this.b
- MandelbrotHalmazNagyító.this.a)
/MandelbrotHalmazNagyító.this.szélesség;
double dy = (MandelbrotHalmazNagyító.this.d
- MandelbrotHalmazNagyító.this.c)
/MandelbrotHalmazNagyító.this.magasság;
// Az új Mandelbrot nagyító objektum elkészítése:
new MandelbrotHalmazNagyító(
MandelbrotHalmazNagyító.this.a+x*dx,
MandelbrotHalmazNagyító.this.a+x*dx+mx*dx,
MandelbrotHalmazNagyító.this.d-y*dy-my*dy,
MandelbrotHalmazNagyító.this.d-y*dy,
600,
MandelbrotHalmazNagyító.this.iterációsHatár);
}
}
});
// Egér mozgás események feldolgozása:
addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
// Vonszolással jelöljük ki a négyzetet:
public void mouseDragged(java.awt.event.MouseEvent m) {
// A nagyítandó kijelölt terület szélessége és magassága:
mx = m.getX() - x;
my = m.getY() - y;
repaint();
}
});
}
/**
* Pillanatfelvételek készítése.
*/
public void pillanatfelvétel() {
// Az elmentendő kép elkészítése:
java.awt.image.BufferedImage mentKép =
Page 386
Számítási mellékletek
358 Created by XMLmind XSL-FO Converter.
new java.awt.image.BufferedImage(szélesség, magasság,
java.awt.image.BufferedImage.TYPE_INT_RGB);
java.awt.Graphics g = mentKép.getGraphics();
g.drawImage(kép, 0, 0, this);
g.setColor(java.awt.Color.BLACK);
g.drawString("a=" + a, 10, 15);
g.drawString("b=" + b, 10, 30);
g.drawString("c=" + c, 10, 45);
g.drawString("d=" + d, 10, 60);
g.drawString("n=" + iterációsHatár, 10, 75);
if(számításFut) {
g.setColor(java.awt.Color.RED);
g.drawLine(0, sor, getWidth(), sor);
}
g.setColor(java.awt.Color.GREEN);
g.drawRect(x, y, mx, my);
g.dispose();
// A pillanatfelvétel képfájl nevének képzése:
StringBuffer sb = new StringBuffer();
sb = sb.delete(0, sb.length());
sb.append("MandelbrotHalmazNagyitas_");
sb.append(++pillanatfelvételSzámláló);
sb.append("_");
// A fájl nevébe belevesszük, hogy melyik tartományban
// találtuk a halmazt:
sb.append(a);
sb.append("_");
sb.append(b);
sb.append("_");
sb.append(c);
sb.append("_");
sb.append(d);
sb.append(".png");
// png formátumú képet mentünk
try {
javax.imageio.ImageIO.write(mentKép, "png",
new java.io.File(sb.toString()));
} catch(java.io.IOException e) {
e.printStackTrace();
}
}
/**
* A nagyítandó kijelölt területet jelző négyzet kirajzolása.
*/
public void paint(java.awt.Graphics g) {
// A Mandelbrot halmaz kirajzolása
g.drawImage(kép, 0, 0, this);
// Ha éppen fut a számítás, akkor egy vörös
// vonallal jelöljük, hogy melyik sorban tart:
if(számításFut) {
g.setColor(java.awt.Color.RED);
g.drawLine(0, sor, getWidth(), sor);
}
// A jelző négyzet kirajzolása:
g.setColor(java.awt.Color.GREEN);
g.drawRect(x, y, mx, my);
}
/**
* Hol áll az egérmutató?
* @return int a kijelölt pont oszlop pozíciója.
*/
public int getX() {
return x;
}
/**
* Hol áll az egérmutató?
* @return int a kijelölt pont sor pozíciója.
*/
public int getY() {
return y;
}
/**
Page 387
Számítási mellékletek
359 Created by XMLmind XSL-FO Converter.
* Példányosít egy Mandelbrot halmazt nagyító obektumot.
*/
public static void main(String[] args) {
// A kiinduló halmazt a komplex sík [-2.0, .7]x[-1.35, 1.35]
// tartományában keressük egy 600x600-as hálóval és az
// aktuális nagyítási pontossággal:
new MandelbrotHalmazNagyító(-2.0, .7, -1.35, 1.35, 600, 255);
}
}
2.5.3. A MandelbrotIterációk osztály
A MandelbrotIterációk osztály alábbi, a 0.0.1 verziója az aktuális. Az alábbi forrásszöveget kijelölve a
MandelbrotIterációk.java forrásállományba kell beillesztenie az Olvasónak a felhasználáshoz.
/*
* MandelbrotIterációk.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* A nagyított Mandelbrot halmazok adott pontjában képes
* nyomonkövetni az z_{n+1} = z_n * z_n + c iterációt.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
*/
public class MandelbrotIterációk implements Runnable{
/** Mennyi időt várakozzunk két iteráció bemutatása között? */
private int várakozás;
// Kissé igaz redundánsan, s nem szépen, de kényelmesen:
private MandelbrotHalmazNagyító nagyító;
private int j, k;
private double a, b, c, d;
private int szélesség, magasság;
private java.awt.image.BufferedImage kép;
/**
* Létrehoz egy iterációkat vizsgáló <code>MandelbrotIterációk</code>
* szál objektumot egy adott <code>MandelbrotHalmaznagyító</code>
* objektumhoz.
*
* @param nagyító egy <code>MandelbrotHalmazNagyító</code> objektum
* @param várakozás várakozási idő
*/
public MandelbrotIterációk(MandelbrotHalmazNagyító nagyító, int várakozás) {
this.nagyító = nagyító;
this.várakozás = várakozás;
j = nagyító.getY();
k = nagyító.getX();
a = nagyító.getA();
b = nagyító.getB();
c = nagyító.getC();
d = nagyító.getD();
kép = nagyító.kép();
szélesség = nagyító.getSz();
magasság = nagyító.getM();
}
/** Az vizsgált pontból induló iterációk bemutatása. */
public void run() {
/* Az alábbi kód javarészt a MandelbrotHalmaz.java számolást
végző run() módszeréből származik, hiszen ugyanazt csináljuk,
csak most nem a hálón megyünk végig, hanem a háló adott a
példányosításunkkor az egérmutató mutatta csomópontjában (ennek
felel meg a c kompelx szám) számolunk, tehát a két külső for
ciklus nem kell. */
Page 388
Számítási mellékletek
360 Created by XMLmind XSL-FO Converter.
// A [a,b]x[c,d] tartományon milyen sűrű a
// megadott szélesség, magasság háló:
double dx = (b-a)/szélesség;
double dy = (d-c)/magasság;
double reC, imC, reZ, imZ, ujreZ, ujimZ;
// Hány iterációt csináltunk?
int iteráció = 0;
// c = (reC, imC) a háló rácspontjainak
// megfelelő komplex szám
reC = a+k*dx;
imC = d-j*dy;
// z_0 = 0 = (reZ, imZ)
reZ = 0;
imZ = 0;
iteráció = 0;
// z_{n+1} = z_n * z_n + c iterációk
// számítása, amíg |z_n| < 2 vagy még
// nem értük el a 255 iterációt, ha
// viszont elértük, akkor úgy vesszük,
// hogy a kiinduláci c komplex számra
// az iteráció konvergens, azaz a c a
// Mandelbrot halmaz eleme
while(reZ*reZ + imZ*imZ < 4 && iteráció < 255) {
// z_{n+1} = z_n * z_n + c
ujreZ = reZ*reZ - imZ*imZ + reC;
ujimZ = 2*reZ*imZ + imC;
// az iteráció (reZ, imZ) -> (ujreZ, ujimZ)
// ezt az egyenest kell kirajzolnunk, de most
// a komplex számokat vissza kell transzformálnunk
// a rács oszlop, sor koordinátájává:
java.awt.Graphics g = kép.getGraphics();
g.setColor(java.awt.Color.WHITE);
g.drawLine(
(int)((reZ - a)/dx),
(int)((d - imZ)/dy),
(int)((ujreZ - a)/dx),
(int)((d - ujimZ)/dy)
);
g.dispose();
nagyító.repaint();
reZ = ujreZ;
imZ = ujimZ;
++iteráció;
// Várakozunk, hogy közben csodálhassuk az iteráció
// látványát:
try {
Thread.sleep(várakozás);
} catch (InterruptedException e) {}
}
}
}
Jar készítése
Kérdés: Hogyan készíthetek olyan Java programot, mely Windows XP alatt az asztalra helyezett
ikonjára kattintva indul el?
Válasz: Készítsünk jar állományt és küldjünk az asztalra egy erre a jar állományra mutató
parancsikont!
C:\...\Munkakönyvtár> javac MandelbrotHalmaz.java
C:\...\Munkakönyvtár> jar cmf manifest.mf mandelbrotos.jar *.class
Page 389
Számítási mellékletek
361 Created by XMLmind XSL-FO Converter.
A fenti fordítás után a jar parancsot használva készítettük el a mandelbrotos.jar Java archívum
állományt. (A jar parancs része a JDK csomagnak.)
2.6. A Pi jegyeinek nyomában
Ez a rész azt segíti, hogy a kedves Olvasó a [KAPCSOLAT REGÉNY] olvasásának befejezésekor keletkezett
lelkesítő feszültségét át tudja vezetni a Pi jegyeinek önálló keresésébe.
A Pi közelítése című pontban megkezdett gombolyag fonalát követjük tovább. A [PI KÖNYV] könyv hívja fel a
figyelmet, hogy Penrose - általunk is sokat hivatkozott - [CSÁSZÁR KÖNYV] könyvében azt valószínűsíti,
hogy a „van-e tíz egymást követő hetes számjegy a Pi tizedes kifejtésében?” kérdésre a választ nem egy konkrét
számítás és keresés, hanem egy egzisztencia bizonyítás adja majd meg. S ezzel szemben 1997-ben Kanada
megtalált egy 7777777777 részsztringet a kifejtésben, ami egyébként a 22.869.046.249. pozíciótól kezdődik!
A következőkben olyan algoritmusokat ismertetünk, amihez a 64 bites lebegőpontos aritmetika is elegendő. Ez
az 1995-ben talált Bailey-Borwein-Plouffe [PI SZÁMÍTÁS], [BBP ALGORITMUS], [PI KÜLDETÉS], [PI
KÖNYV] féle (röviden BBP) algoritmus, aminek további meglepő érdekessége, hogy a Pi hexadecimális
kifejtésében egy adott pozíciótól tudunk jegyeket meghatározni, a korábbi jegyek ismerete nélkül!
/*
* PiBBP.java
*
* DIGIT 2005, Javat tanítok
* Bátfai Norbert, [email protected]
*
*/
/**
* A BBP (Bailey-Borwein-Plouffe) algoritmust a Pi hexa
* jegyeinek számolását végző osztály. A könnyebb olvahatóság
* kedvéért a változó és metódus neveket megpróbáltuk az algoritmust
* bemutató [BBP ALGORITMUS] David H. Bailey: The BBP Algorithm for Pi.
* cikk jelöléseihez.
*
* @author Bátfai Norbert, [email protected]
* @version 0.0.1
*/
public class PiBBP {
/** A Pi hexa kifejtésében a d+1. hexa jegytől néhány hexa jegy.*/
String d16PiHexaJegyek;
/**
* Létrehoz egy <code>PiBBP</code>, a BBP algoritmust a Pi-hez
* alkalmazó objektumot. A [BBP ALGORITMUS] David H. Bailey: The
* BBP Algorithm for Pi. alapján a
* {16^d Pi} = {4*{16^d S1} - 2*{16^d S4} - {16^d S5} - {16^d S6}}
* kiszámítása, a {} a törtrészt jelöli.
*
* @param d a Pi hexa kifejtésében a d+1. hexa jegytől
* számoljuk a hexa jegyeket
*/
public PiBBP(int d) {
double d16Pi = 0.0d;
double d16S1t = d16Sj(d, 1);
double d16S4t = d16Sj(d, 4);
double d16S5t = d16Sj(d, 5);
double d16S6t = d16Sj(d, 6);
d16Pi = 4.0d*d16S1t - 2.0d*d16S4t - d16S5t - d16S6t;
d16Pi = d16Pi - StrictMath.floor(d16Pi);
StringBuffer sb = new StringBuffer();
Page 390
Számítási mellékletek
362 Created by XMLmind XSL-FO Converter.
Character hexaJegyek[] = {'A', 'B', 'C', 'D', 'E', 'F'};
while(d16Pi != 0.0d) {
int jegy = (int)StrictMath.floor(16.0d*d16Pi);
if(jegy<10)
sb.append(jegy);
else
sb.append(hexaJegyek[jegy-10]);
d16Pi = (16.0d*d16Pi) - StrictMath.floor(16.0d*d16Pi);
}
d16PiHexaJegyek = sb.toString();
}
/**
* BBP algoritmus a Pi-hez, a [BBP ALGORITMUS] David H. Bailey: The
* BBP Algorithm for Pi. alapján a {16^d Sj} részlet kiszámítása.
*
* @param d a d+1. hexa jegytől számoljuk a hexa jegyeket
* @param j Sj indexe
*/
public double d16Sj(int d, int j) {
double d16Sj = 0.0d;
for(int k=0; k<=d; ++k)
d16Sj += (double)n16modk(d-k, 8*k + j) / (double)(8*k + j);
/* (bekapcsolva a sorozat elejen az első utáni jegyekben növeli pl.
a pontosságot.)
for(int k=d+1; k<=2*d; ++k)
d16Sj += StrictMath.pow(16.0d, d-k) / (double)(8*k + j);
*/
return d16Sj - StrictMath.floor(d16Sj);
}
/**
* Bináris hatványozás mod k, a 16^n mod k kiszámítása.
*
* @param n kitevő
* @param k modulus
*/
public long n16modk(int n, int k) {
int t = 1;
while(t <= n)
t *= 2;
long r = 1;
while(true) {
if(n >= t) {
r = (16*r) % k;
n = n - t;
}
t = t/2;
if(t < 1)
break;
r = (r*r) % k;
}
return r;
}
/**
Page 391
Számítási mellékletek
363 Created by XMLmind XSL-FO Converter.
* A kiszámolt néhány hexa jegy visszaadása. A használt lebegőpontos
* aritmentia függvényében mondjuk az első 6 pontos peldául
* d=1000000 esetén.
*
* @return String a kiszámolt néhány hexa jegy
*/
public String toString() {
return d16PiHexaJegyek;
}
/** Példányosít egy BBP algoritmust implementáló obektumot.*/
public static void main(String args[]) {
System.out.print(new PiBBP(1000000));
}
}
Fordítva és futtatva a példát azt kapjuk, hogy a Pi hexadecimális kifejtése az 1000001. jegytől a következő:
...6C65E5308...
[norbi@niobe ~]$ javac PiBBP.java
[norbi@niobe ~]$ java PiBBP
6C65E5308
(C-ben a long double típus használata mellett ugyanezzel a futtatással több jegy pontosság érhető el:
...6C65E52CB858...)
A PiBBP osztály felhasználásával elkezdhetjük folyamatosan vizsgálni adott pozíciótól a Pi hexadecimális
kifejtésének jegyeit, az osztály main() indítófüggvényét módosítva:
public static void main(String args[]) {
for(int i=1000000; i<1001000; i+=2) {
PiBBP piBBP = new PiBBP(i);
System.out.print(piBBP.toString().charAt(0));
System.out.print(piBBP.toString().charAt(1));
}
}
Fordítás és futtatás után
[norbi@niobe ~]$ javac PiBBP.java
[norbi@niobe ~]$ java PiBBP
6C65E52CB459350050E4BB178F4C67A0FCF7BF27206290FBE70F93B828CD939C475C728F2FDB0CB9
23CF52C40D631D4DB2E98340AA25A6F07DB685C0A9C04F3F6E667CFD6E1764C83ECA94E79661FC18
0E6AEF581987E79E13278712CB01255E8CE4D9E048F782D756370548FB0778323CF2074C2716D121
639F1DD5A31EF6C242676B3783AD528852CCA52A9B4F999C526B0750859AEEC9CE6635B30996A210
CD419D5FD47A4E7AAF906E26A4CCF99A2E493BBB5E7D5E0B94F15196DA8CD1A0C57FE03A629B2D58
42317C173D163EA8717B46930EE0FE82FEC4B01016F155FB446AA6958EAD9265EC0C914CB84755DD
1BCE5100C23804D67A787BEC57CD7D8E190B3F55E3D2558927215504F141AC8B0BA836F7781E1966
4EFA8B22BEB3816A70F7210E4784A1F37762361286448CD051BCE3A4CE156D70CDBA256C1A36C386
48633C8F13A53405795635084A2DEAF3B9066BC3863BB07447DDDBDE5644034A6893E3E1CFDB3696
31BAA4240D93F17F667F7C51ABF076F7C1BB35DECC240153F4817A579CBD1DAC895E8555929D1ADA
3C787A0BF2881BBC44C4BE505E91FE5A28B9BA47D4845B7639239AD71D8B63BF9D23B2CC88C9D39C
033B0482F5F801D778BBB734EA8B1BE878D129514BFA5C4A6D60E80CF4B14A2A5673992B18397230
54BD44F767B03245F2873973EF6D84B2B96EFC9A
Page 392
Számítási mellékletek
364 Created by XMLmind XSL-FO Converter.
kaptuk például a Pi hexadecimális kifejtésének 1000001. pozíciójától a kifejtés 1000 hexadecimális számjegyét.
A PiBBP osztály felhasználásával természetesen elölről is elkezdhetjük folyamatosan vizsgálni a Pi
hexadecimális kifejtésének jegyeit, az osztály main() indítófüggvényét ehhez így módosítva:
public static void main(String args[]) {
for(int i=0; i<3000; i+=1) {
PiBBP piBBP = new PiBBP(i);
System.out.print(piBBP.toString().charAt(0));
}
}
Fordítás és futtatás után
[norbi@niobe ~]$ javac PiBBP.java
[norbi@niobe ~]$ java PiBBP
243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89452821E638D01377
BE5466CF34E90C6CC0AC29B7C97C50DD3F84D5B5B54709179216D5D98979FB1BD1310BA698DFB5AC
2FFD72DBD01ADFB7B8E1AFED6A267E96BA7C9045F12C7F9924A19947B3916CF70801F2E2858EFC16
636920D871574E69A458FEA3F4933D7E0D95748F728EB658718BCD5882154AEE7B54A41DC25A59B5
9C30D5392AF26013C5D1B023286085F0CA417918B8DB38EF8E79DCB0603A180E6C9E0E8BB01E8A3E
D71577C1BD314B2778AF2FDA55605C60E65525F3AA55AB945748986263E8144055CA396A2AAB10B6
B4CC5C341141E8CEA15486AF7C72E993B3EE1411636FBC2A2BA9C55D741831F6CE5C3E169B87931E
AFD6BA336C24CF5C7A325381289586773B8F48986B4BB9AFC4BFE81B6628219361D809CCFB21A991
487CAC605DEC8032EF845D5DE98575B1DC262302EB651B8823893E81D396ACC50F6D6FF383F44239
2E0B4482A484200469C8F04A9E1F9B5E21C66842F6E96C9A670C9C61ABD388F06A51A0D2D8542F68
960FA728AB5133A36EEF0B6C137A3BE4BA3BF0507EFB2A98A1F1651D39AF017666CA593E82430E88
8CEE8619456F9FB47D84A5C33B8B5EBEE06F75D885C12073401A449F56C16AA64ED3AA62363F7706
1BFEDF72429B023D37D0D724D00A1248DB0FEAD349F1C09B075372C980991B7B25D479D8F6E8DEF7
E3FE501AB6794C3B976CE0BD04C006BAC1A94FB6409F60C45E5C9EC2196A246368FB6FAF3E6C53B5
1339B2EB3B52EC6F6DFC511F9B30952CCC814544AF5EBD09BEE3D004DE334AFD660F2807192E4BB3
C0CBA85745C8740FD20B5F39B9D3FBDB5579C0BD1A60320AD6A100C6402C7279679F25FEFB1FA3CC
8EA5E9F8DB3222F83C7516DFFD616B152F501EC8AD0552AB323DB5FAFD23876053317B483E00DF82
9E5C57BBCA6F8CA01A87562EDF1769DBD542A8F6287EFFC3AC6732C68C4F5573695B27B0BBCA58C8
E1FFA35DB8F011A010FA3D98FD2183B84AFCB56C2DD1D35B9A53E479B6F84565D28E49BC4BFB9790
E1DDF2DAA4CB7E3362FB1341CEE4C6E8EF20CADA36774C01D07E9EFE2BF11FB495DBDA4DAE909198
EAAD8E716B93D5A0D08ED1D0AFC725E08E3C5B2F8E7594B78FF6E2FBF2122B648888B812900DF01C
4FAD5EA0688FC31CD1CFF191B3A8C1AD2F2F2218BE0E1777EA752DFE8B021FA1E5A0CC0FB56F74E8
18ACF3D6CE89E299B4A84FE0FD13E0B77CC43B81D2ADA8D9165FA2668095770593CC7314211A1477
E6AD206577B5FA86C75442F5FB9D35CFEBCDAF0C7B3E89A0D6411BD3AE1E7E4900250E2D2071B35E
226800BB57B8E0AF2464369BF009B91E5563911D59DFA6AA78C14389D95A537F207D5BA202E5B9C5
832603766295CFA911C819684E734A41B3472DCA7B14A94A1B5100529A532915D60F573FBC9BC6E4
2B60A47681E6740008BA6FB5571BE91FF296EC6B2A0DD915B6636521E7B9F9B6FF34052EC5855664
53B02D5DA99F8FA108BA47996E85076A4B7A70E9B5B32944DB75092EC4192623AD6EA6B049A7DF7D
9CEE60B88FEDB266ECAA8C71699A17FF5664526CC2B19EE1193602A575094C29A0591340E4183A3E
3F54989A5B429D656B8FE4D699F73FD6A1D29C07EFE830F54D2D38E6F0255DC14CDD20868470EB26
6382E9C6021ECC5E09686B3F3EBAEFC93C9718146B6A70A1687F358452A0E286B79C5305AA500737
3E07841C7FDEAE5C8E7D44EC5716F2B8B03ADA37F0500C0DF01C1F040200B3FFAE0CF51A3CB574B2
25837A58DC0921BDD19113F97CA92FF69432477322F547013AE5E58137C2DADCC8B576349AF3DDA7
A94461460FD0030EECC8C73EA4751E41E238CD993BEA0E2F3280BBA1183EB3314E548B384F6DB908
6F420D03F60A04BF2CB8129024977C795679B072BCAF89AFDE9A771FD9930810B38BAE12DCCF3F2E
5512721F2E6B7124501ADDE69F84CD877A5847187408DA17BC9F9ABCE94B7D8CEC7AEC3ADB851DFA
63094366C464C3D2EF1C18473215D908DD433B3724C2BA1612A14D432A65C45150940002133AE4DD
71DFF89E10314E5581AC77D65F11199B043556F1
megkapjuk a Pi hexadecimális kifejtésének 3000 jegyét, azaz hexában a 3.243F6....
Hosszabb számítások végzéséhez a kevésbé objektumorientált jellegű PiBBPBench osztály használatát
javasoljuk, az osztályt az Egyszerű összehasonlítások a sebesség kérdésében című pontban találja meg az
érdeklődő Olvasó.
Page 393
365 Created by XMLmind XSL-FO Converter.
Irodalomjegyzék
Idézetek, bevezető részek
[MÁTRIX MOZI] Wachowski, Andy és Wachowski, Larry. The Matrix. http://www.imdb.com/title/tt0133093/
. 1999.
[KVANTUM MOZI] Arntz, William és Chasse, Betsy. What the #$*! Do We (K)now!? Mi a csudát tudunk a
világról?. http://www.imdb.com/title/tt0399877/ http://www.whatthebleep.com/ . 2004.
[DOOM JÁTÉK] DOOM. http://www.idsoftware.com/ .
[MARX KÖNYV] Marx, György. Gyorsuló idő. Typotex. 2005.
[DOOM KÖNYV] Kushner, David. A DOOM LEGENDÁJA. VogelBurda. 2004.
[SZÁMÉRZÉK KÖNYV] Dehaene, Stanislas. A számérzék. Osiris. 2003.
[VASSY JEGYZET] Vassy, Zoltán. Schrödingerék macskája és más történetek.
http://vmek.oszk.hu/00500/00571/html/ .
[KERNIGHAN PROG KÖNYV] Kernighan, Brian W. és Plauger, P. J.. A programozás magasiskolája.
Műszaki. 1982.
[KERNIGHAN C KÖNYV] Kernighan, Brian W. és Ritchie, Dennis M.. A C programozási nyelv. Műszaki.
1993.
[WIGNER KÖNYV] Wigner, Jenő. Szimmetriák és reflexiok. Gondolat. 1972.
[TELLER LEVÉL] Jones, Eric M.. „Where is everybody”An Account of Fermi's Question.
http://www.fas.org/sgp/othergov/doe/lanl/la-10311-ms.pdf .
Informatikai, fizikai, matematikai jellegű ismeretterjesztés
[CSÁSZÁR KÖNYV] Penrose, Roger. A császár új elméje. Akadémiai. 1993.
[PENROSE-HAWKING KÖNYV] Penrose, Roger és Hawking, Stephen. A nagy, a kicsi és az emberi elme.
Akkord. 2003.
[STEWART KÖNYV] Stewart, Ian. A matematika problémái. Akadémiai. 1991.
[SZÁMÉRZÉK KÖNYV] Dehaene, Stanislas. A számérzék. Osiris. 2003.
[PARADOXON KÖNYV] Székely J., Gábor. Paradoxonok a véletlen matematikájában. Typotex. 2004.
[PÉNZ KÖNYV] Mérő, László. Az élő pénz. Tercium. 2004.
[ISTEN KÖNYV] Davies, Paul. Isten gondolatai. Kulturtrade. 1995.
Automata, algoritmus és információelmélet
[ALGORITMUSOK KÖNYV] Rónyai, Lajos. ALGORITMUSOK. Typotex. 1998.
[LOVÁSZ KÖNYV] Lovász, László. Algoritmusok bonyolultsága. Nemzeti. 1994.
Page 394
Irodalomjegyzék
366 Created by XMLmind XSL-FO Converter.
[CHAITIN OMEGA] Chaitin, Gregory. A theory of program size formally identical to information theory.
Journal of the ACM http://www.cs.auckland.ac.nz/CDMTCS/chaitin/acm75.pdf
http://citeseer.ist.psu.edu/chaitin75theory.html . 22. 329-340. 1975.
[CHAITIN OMEGA 1] Chaitin, Gregory. META MATH! The Quest for Omega.
http://www.cs.auckland.ac.nz/CDMTCS/chaitin/omega.html .
[CHAITIN OMEGA 2] Chaitin, Gregory. THE LIMITS OF REASON.
http://www.cs.auckland.ac.nz/CDMTCS/chaitin/sciamer3.html .
[CHAITIN OMEGA n] Chaitin, Gregory. Omega and why maths has no TOEs.
http://plus.maths.org/issue37/features/omega/ .
[CALUDE KÖNYV] Calude, Cristian S.. Information and Randomness An Algorithmic Perspective. Springer.
2002.
[JAVA PROG és OMEGA] Calude, Cristian S., Dinneen, Michael J., és Shu, Chi-Kou. Computing a Glimpse of
Randomness. Experimental Mathematics http://www.cs.auckland.ac.nz/~cristian/Calude361_370.pdf
http://arxiv.org/abs/nlin.cd/0112022 . 2002.
[JAVA PROG és OMEGA 2] Shu, Chi-Kou. Computing Exact Approximations of a Chaitin Omega Number.
Ph.D. Thesis, University of Auckland, New Zealand . 2003.
[BENNETT CIKK] Bennett, Charles H.. On Random and Hard-to-Describe Numbers.
http://citeseer.ist.psu.edu/429077.html . 1979.
[VITÁNYI KÖNYV] Li, Ming és Vitányi, Paul. An Introduction to Kolmogorov Complexity and its
Applications. Springer. 1993.
[VITÁNYI HASONLÓSÁG CIKK] Chen, Xin, Li, Xin, Ma, Bin, és Vitányi, Paul. The Similarity Metric. IEEE
Transactions on Information Theory http://www.cwi.nl/~paulv/papers/similarity.pdf
http://arxiv.org/abs/cs.CC/0111054 . 50. 12. 2004.
[TURING CIKK] Turing, Alan. On computable numbers, with an application to the Entscheidungsproblem.
Proceedings of the London Mathematical Society http://web.comlab.ox.ac.uk/oucl/research/areas/ieg/e-
library/sources/tp2-ie.pdf . 2. 42. 1936.
[TURING GÉP REPREZENTÁCIÓ] Juhász, István és Szalai, Ferenc. Turing gép az általános iskolában. Frey
Tamás Vándorgyűlés, Kecskemét.. 1989.
[DRAGÁLIN KÖNYV] Dragálin, Albert és Búzási, Szvetlána. Bevezetés a matematikai logikába. Kossuth
Egyetemi Kiadó. 1996.
[MOBIL JÁTÉK ÉLMÉNY] Bátfai, Norbert és Bátfai, Erika. A mobil játékfejlesztés elméleti és gyakorlati
momentumai. Híradástechnika. LX. 34-36. 2005/5.
[BENCZÚR CIKK] Benczúr, András. The Evolution of Human Communication and the Information Revolution
- A Mathematical Perspective. Mathematical and Computer Modeling. 38. 691-708. 2003.
[SHANNON INFÓELM CIKK] Shannon, C. E.. A Mathematical Theory of Communication. The Bell System
Tech. Journal http://cm.bell-labs.com/cm/ms/what/shannonday/paper.html . 27. 379-423. 1984.
[DEMETROVICS KÖNYV] Demetrovics, János, Jordan, Denev, és Radiszlav, Pavlov. A számítástudomány
matematikai alapjai. Nemzeti Tankönyvkiadó. 1985.
[CHOMSKY NYELVOSZTÁLYOK] Chomsky, Noam. Three Models For The Description of Language.
http://www.chomsky.info/articles.htm .
Programozás
Page 395
Irodalomjegyzék
367 Created by XMLmind XSL-FO Converter.
[PROGRAMOZÓ PÁTERNOSZTER JEGYZET] Bátfai, Norbert. Programozó Pátermoszter jegyzet.
http://www.javacska.hu/ProgramozoPaternoszter.pdf
http://www.inf.unideb.hu/~nbatfai/ProgramozoPaternoszter.pdf .
[PICI PROGRAMOZÁS I JEGYZET] Juhász, István. Programozás 1 egyetemi jegyzet.
http://infotech.inf.unideb.hu/juhasz/ .
[PICI PROGRAMOZÁS II JEGYZET] Juhász, István. Programozás 2 egyetemi jegyzet.
http://infotech.inf.unideb.hu/juhasz/ .
[PROGRAMOZÁS KÖNYV] Nyékyné Gaizler, Judit. Programozási nyelvek. Kiskapu. 2003.
[LEVÉNEZ IDŐVONALAK] Lévénez, Éric. Programming Languages. http://www.levenez.com/lang/ .
[SEBESTA KÖNYV] Sebesta, Robert W.. Concepts of programming languages. The Benjamin/Cumming.
1993.
[SEBESTA ÚJ KÖNYV] Sebesta, Robert W.. Concepts of programming languages. Addison-Wesley. 2004.
[JAVA ás C SHARP] Chandra, Shyamal Suhana és Chandra, Kailash. A comparison of Java and C Sharp.
Journal of Computing Sciences in Colleges http://portal.acm.org/ . 20. 3. 238-254. 2005.
[C SHARP J--] Johnson, Mark. C Sharp: A language alternative or just J--?.
http://www.javaworld.com/javaworld/jw-11-2000/jw-1122-csharp1.html . 2000.
[.NET Framework SDK 2.0] .NET Framework Developer Center. http://www.microsoft.com/netframework .
[C SHARP ALAP KÖNYV] Hejlsberg, Anders, Wiltamuth, Scott, és Golde, Peter. The C Sharp Programming
Language. Addison-Wesley. 2006.
[C SHARP KÖNYV] Illés, Zoltán. Programozás C Sharp nyelven. Jedlik Oktatási Stúdió. 2004.
[C SHARP KÖNYV 2] Mayo, Joseph. C Sharp Unleashed. SAMS. 2002.
[C SHARP KÖNYV 3] Sharp, John és Jagger, Jon. Microsoft Visual C Sharp .NET. Microsoft Press. 2002.
[SOMMERVILLE KÖNYV] Sommerville, Ian. Szoftverrendszerek fejlesztése. Panem. 2002.
Java programozás
[JAVA.SUN.COM] Java Technology. http://java.sun.com .
[JAVA.COM] java.com: Hot Games, Cool Apps. http://java.com .
[JAVA KÖNYV] Nyékyné Gaizler, Judit. Java 2 útikalauz programozóknak. ELTE TTK Hallgatói Alapítvány.
2000.
[JAVA EE KÖNYV] Nyékyné Gaizler, Judit. J2EE Útikalauz Java programozóknak. ELTE TTK Hallgatói
Alapítvány. 2002.
[JAVA 5 KÖNYV] Gaddis, Tony. Starting out with Java 5 Early Objects. Addison Wesley. 2005.
[KILLER KÖNYV] Davison, Andrew. Killer Game Programming. O'Reilly. 2005.
[J2ME KÖNYV] Piroumian, Vartan. Wireless J2ME Platform Programming. Prentice Hall. 2005.
[JAVA EE 5] Jendrock, Eric és al., et.. The Java EE 5 Tutorial . http://java.sun.com/javaee/5/docs/tutorial/doc .
2006.
[JAVA JÁTÉK] Twilleager, Doug, Kesselman, Jeff, Goldberg, Athomas, Petersen, Daniel, Soto, Juan Carlos, és
Melissinos, Chris. Java Technologies for Games. ACM Computers in Entartainment
http://portal.acm.org . 2. 2. 2004.
Page 396
Irodalomjegyzék
368 Created by XMLmind XSL-FO Converter.
[MIDP 2.0 JÁTÉKOK] Mahmoud, Qusay. Getting Started With the MIDP 2.0 Game API.
http://developers.sun.com/techtopics/mobility/midp/articles/gameapi .
[JAVA PÉLDA WEBSZERVER] Brown, David. A Simple Multithreaded Web Server.
http://java.sun.com/developer/technicalArticles/Networking/Webserver/index.html .
[OAK] Byous, Jon. Java Technology: the Early Years. http://java.sun.com/features/1998/05/birthday.html .
[JAVA MINDENÜTT] Java Everywhere. http://www.sun.com/java/everywhere .
[GOSLING INTERJÚ] Eckstein, Robert. James Gosling on Open Sourcing Sun's Java Platform
Implementations. http://java.sun.com/developer/technical/Articles/Intervoews/gosling_os1_qa.html .
2000.
[HÁLÓZATI JAVA] Reilly, David és Reilly, Michael. Java Network Programming and Distributed Computing.
Addison-Wesley. 2002.
[JAVA DOKUMENTÁCIÓ] JDK 6 Documentation. http://java.sun.com/javase/6/download.jsp#docs .
[SUN JAVA CIKKEK] Core Java Technologies Tech Tips. http://java.sun.com/developer/JDCTechTips/ .
[SUN JAVA MOBIL CIKKEK] J2ME Technical Articles and Tips.
http://developers.sun.com/techtopics/mobility/reference/techart/index.html .
[FORUM NOKIA CIKKEK] Forum Nokia - resources for mobile application developers.
http://forum.nokia.com/ .
[MOBIL PROG EA] Bátfai, Norbert. A mobil játékfejlesztés elméleti és gyakorlati momentumai. Gyires Béla
Informatikai Nap, Debrecen http://www.inf.unideb.hu/kutatas/gybin/gybin04/Batfai_Norbert.pdf .
2005.
[MOBIL PROG SUN EA] Bátfai, Norbert és Bátfai, Erika. A Java mobiljáték-fejlesztés elmélete és gyakorlata
és a kék (JSR 82) játékok. 5. Sun Java Fejlesztői Nap, Budapest. 2005.
[JAVA START] Vég, Csaba és Juhász, István. Java Start!. Logos 2000. 1999.
Hálózatok, internet
[HÁLÓZATOK KÖNYV] Tanenbaum, Andrew S.. Számítógép-hálózatok. Panem. 2004.
[HÁLÓZATOS JAVA KÖNYV] Csizmazia, Balázs. Hálózati alkalmazások készítése : CORBA, Java, WWW.
Kalibán BT. 1998.
[CORBA SPECIFIKÁCIÓ] CORBA. http://www.omg.org/technology/documents/formal/corba_iiop.htm .
[COSNAMING IDL] cosnaming.idl. http://www.omg.org/docs/formal/04-10-07.txt .
[NYELVI LEKÉPEZÉS] Catalog of OMG IDL / Language Mappings Specifications.
http://www.omg.org/technology/documents/idl2x_spec_catalog.htm
http://www.omg.org/docs/formal/02-08-05.pdf .
[INTERNET REGULATION] Clark, David. Social Protocols, Design Principles, and Analytic Methods.
http://www.w3.org/Talks/1999/0105-Regulation/all.htm .
[PORTOK] IANA port assignments. http://www.iana.org/assignments/port-numbers .
[RFC] Request for Comments. http://www.ietf.org/rfc .
[RFC 822] STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES. http://www.ietf.org/rfc
.
[RFC 2616] Hypertext Transfer Protocol - HTTP/1.1. http://www.ietf.org/rfc .
Page 397
Irodalomjegyzék
369 Created by XMLmind XSL-FO Converter.
Máselvű programozás
[DES TÖRÉS] Boneh, Dan. Breaking DES Using a Molecular Computer.
http://citeseer.ist.psu.edu/boneh95breaking.html .
[DNA SZÁMÍTÁS] Adleman, Leonard M.. Molecular Computation Of Solutions To Combinatorial Problems.
http://citeseer.ist.psu.edu/adleman94molecular.html .
Kvantum számítások
[ORCH OR] Hameroff, Stuart és Penrose, Roger. Orchestrated Objective Reduction of Quantum Coherence in
Brain Microtubules: The „Orch OR” Model for Consciousmess.
http://www.quantumconsciousness.org/penrose-hameroff/orchOR.html .
[ORCH OR TUDAT] Hameroff, Stuart és Penrose, Roger. Conscious Events as Orchestrated Space-Time
Selections. http://www.quantumconsciousness.org/penrose-hameroff/consciousevents.html .
[TEGMARK DEKOHERENCIA CIKK] Tegmark, M.. Importance of quantum decoherence in brain processes.
Physical Review E. http://prola.aps.org/abstract/PRE/v61/i4/p4194_1 . 65. 2002.
[TEGMARK DEKOHERENCIA VÁLASZ CIKK] Hameroff, Stuart R.. Quantum computation in brain
microtubules: Decoherence and biological feasibility. Physical Review E.
http://prola.aps.org/abstract/PRE/v65/i6/e061901 http://arxiv.org/abs/quant-ph/0005025 . 65. 2002.
[VÉLETLEN MŰSZER] Quantum Random Numbers Generator.
http://www.gapoptic.unige.ch/Prototypes/QRNG/ .
[KVANTUM TUDAT] atmanspacher, Harald. Quantum Theory and Consciousness: an Overview with Selected
Examples. 1. Discrete Dynamics in Nature and Society
http://www.hindawi.com/GetArticle.aspx?doi=10.1155/S102602260440106X . 2004.
[DEUTSCH KVANTUMGÉP CIKK] Deutsch, David. Quantum theory, the Church-Turing principle and the
universal quantum computer. Proceedings of the Royal Society of London A
http://citeseer.ist.psu.edu/deutsch85quantum.html . 400. 97-117. 1985.
[KVANTUMINFORMATIKA] Sailer, Kornél. Kvantuminformatika. http://dtp.atomki.hu/HOME-
PAGE/lectures/kvinfl.pdf http://dtp.science.unideb.hu/hun/jegyzetek.php . 1985.
Biológiai jellegű
[GÉP és AGY] Neumann, János. A számológép és az agy. Gondolat. 1964.
[HAMEROFF MIKROTUBULUS] Hameroff, Stuart R. és Watt, Richard C.. Information Processing in
Microtubules. Journal of Theoretical Biology http://www.ncbi.nlm.nih.gov/entrez/ . 98. 549-561.
1982.
[HAMEROFF INTERJÚ] Hameroff, Stuart R. és Gabora, Liane. Microtubules, Anesthetics, and Quantum
Consciousness: An Interview with Stuart Hameroff. Foundations of Science
http://www.springerlink.com/index/X55154J16754W370.pdf . 4. 205-223. 1999.
[HANGYÁK és KOLMOGOROV CIKK] Ryabko, Boris és Reznikova, Zhanna. Using Shannon Entropy and
Kolmogorov Complexity To Study the Communicative System and Cognitive Capacities in Ants.
http://boris.ryabko.net/papers.html .
[C. ELEGANS] Kitano, Hiroaki, Hamahashi, Shugo, Kitazawa, Jun, és Luke, Sean. The Perfect C. elegans
Project: An Initial Report. http://citeseer.ist.psu.edu/120528.html .
[UniProtKB/Swiss-Prot] ExPASy Proteomics Server. http://www.expasy.org .
[HUMÁN GENOM PROJEKT] Humán Genom Projekt. http://www.ncbi.nlm.nih.gov/genome/seq .
Page 398
Irodalomjegyzék
370 Created by XMLmind XSL-FO Converter.
[SEJTBIO 1] Csaba, György. Sejtbiológia. Medicina. 1990.
[SEJTBIO 2] Szeberényi, József. Molekuláris sejtbiológia. Dialóg Campus. 1999.
[MIKROTUBULUS 1] Karafyllidis, Ioannis G. és Lagoudas, Dimitris C.. Microtubules as mechanical force
sensors. BioSystems http://www.ncbi.nlm.nih.gov/entrez PMID: 16806669 . 2006.
[BIOINFORMATIKA] Maróti, Péter. Információelmélet a biológiában. JATEPress. 2003.
[PROTEOMIKA] Campbell, A. Malcolm és Heyer, Laurie J.. Genomika, proteomika, bioinformatika. Medicina.
2004.
[GONDOLKODÓ KÖNYV] Calvin, William H.. A gondolkodó agy. Kulturtrade. 1996.
Matematikai jellegű
[KNUTH 2. KÖNYV] Knuth, Donald E.. A számítógép programozás művészete 2.. Műszaki. 1994.
[MATEK JÁTÉK] Csákány, Béla. Diszkrét matematikai játékok. Polygon. 171-173. 1998.
[ÉLET CIKK] Wainwright, Robert T.. Life is Universal. http://portal.acm.org/citation.cfm?id=800290.811303 .
1974.
[VÉLETLEN VÉLETLEN] Révész, Pál. Mennyire véletlen a véletlen?. Akadémiai. 14. 1984.
[VÉLETLEN KÖNYV] Deák, István. Véletlenszám generátorok és alkalmazásuk. Akadémiai. 1986.
[BARNSLEY KÖNYV] Barnsley, M.. Fractals everywhere. Academic Press, Boston. 1986.
[FRAKT. SZAKDOLG.] Bátfai, Norbert. Szimultán számrendszerekkel generált fraktálok ábrázolása. KLTE
szakdolgozat, témavezető Boros Zoltán. 1996.
[PI KÖNYV] Berggren, J. Lennart, Borwein, Jonathan M., és Borwein, Peter B.. A Pamphlet on Pi serving as a
Supplement for the Third Edition of Pi: A Source Book. http://citeseer.ist.psu.edu/589901.html . 2003.
[PI SZÁMÍTÁS] Bailey, David H., Borwein, Peter B., és Plouffe, Simon. On The Rapid Computation of
Various Polylogarithmic Constants. Mathematics of Computation
http://citeseer.ist.psu.edu/bailey96rapid.html . 66. 218. 903-913. 1997.
[PI KÜLDETÉS] Bailey, David H., Borwein, Jonathan M., Borwein, Peter B., és Plouffe, Simon. The Quest for
Pi. Mathematical Intelligencer http://citeseer.ist.psu.edu/bailey96quest.html . 19. 1. 50-57. 1996.
[BBP ALGORITMUS] Bailey, David H.. The BBP Algorithm for Pi.
http://crd.lbl.gov/~dhbailey/dhbpapers/bbp-alg.pdf . 2006.
[NEUMANN KVANTUM KÖNYV] Neumann, János. A kvantummechanika matematikai alapjai. Akadémiai.
1980.
[RÉNYI VALSÉG KÖNYV] Rényi, Alfréd. Valószínűségszámítás. Tankönyvkiadó. 144. 1973.
Informatika gyerekeknek, diákoknak
[JÁVÁCSKA BARÁTAI] Bátfai, Norbert. Jávácska Barátai, Gyermek - Robot Barátság.
http://www.javacska.hu .
[JÁVÁCSKA PORTÁL] Bátfai, Norbert és Bátfai, Erika. Jávácska Portál. http://javacska.lib.unideb.hu .
[II. JÁVÁCSKA] , . II. Jávácska Konferencia. http://javacska.lib.unideb.hu/konf2 .
[TANÁRKÉPZÉS EA.] Bátfai, Norbert és Bátfai, Erika. Jávácska és az informatika tanárképzés.
http://javacska.lib.unideb.hu/ea/IF_2005/Javacska_InfoTanarkepzes_If2005.pdf .
Page 399
Irodalomjegyzék
371 Created by XMLmind XSL-FO Converter.
[FANTASZTIKUS PROGRAMOZÁS] Bátfai, Norbert és Bátfai, Erika. Fantasztikus programozás. Debreceni
Egyetem Egyetemi és Nemzeti Könyvtár http://javacska.lib.unideb.hu/konyv/bv-naploja-kezirat-I-
5_0_0.pdf . 2004.
[LEGO MINDSTORMS] LEGO.com MINDSTORMS NXT Home. http://mindstorms.lego.com/ .
[LEJOS JAVA] leJOS - Java for Lego Mindstorms. http://www.lejos.org http://lejos.sourceforge.net/ .
[LEGO és NI] LEGO MINDSTORMS NXT - Powered by NI LabVIEW.
http://www.ni.com/academic/mindstorms/ .
Tudományos közösségek és szórakoztató ismeretterjesztés
[KAPCSOLAT MOZI] Zemeckis, Robert. Contact. http://www.imdb.com/title/tt0118884/ . 1997.
[KAPCSOLAT REGÉNY] Sagan, Carl. Kapcsolat. Édesvíz. 1993.
[SETI HOME] SETI@home. http://setiathome.ssl.berkeley.edu .
[ASIMOV REGÉNY] Asimov, Isaac. Én, a robot. Szukits. 2004.
[ASIMOV MOZI] Proyas, Alex. Én, a robot. http://www.imdb.com/title/tt0343818/ . 2004.
A szokásos paradigmákon túl
[KVANTUM JÓGA] Goswami, Amit. Képzelt ablak. Édesvíz. 2005.
[PROGRAMOZOTT VÍZ] Emoto, Masaru. A víz rejtett bölcsessége. Édesvíz. 2005.
Általános
[WIKI] , . Wikipedia, the free encyclopedia. Wikipedia .