· web viewjavát tanítok. bevezetés a programozásba a turing gépektől a corba...

481
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]> Created by XMLmind XSL-FO Converter.

Upload: voque

Post on 05-Oct-2018

214 views

Category:

Documents


0 download

TRANSCRIPT

Javát tanítokBevezeté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]>

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 2007Szerző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é.

Created by XMLmind XSL-FO Converter.

AjánlásKeresztmamának.

Created by XMLmind XSL-FO Converter.

TartalomBevezetés ............................................................................................................................................. 7

1. A szerzőkről ............................................................................................................................ 72. Előszó ..................................................................................................................................... 8

2.1. Bíztatás ....................................................................................................................... 92.1.1. Előismeretek .................................................................................................. 9

2.2. Javasolt használati esetek ........................................................................................... 92.2.1. A még teljesen kezdő és csak a Java programozás iránt érdeklődőkről szóló használati eset ....................................................................................................... 102.2.2. Csak a Java programozás oktatása iránt érdeklődőkről szóló használati eset 102.2.3. Szekvenciális feldolgozással kezdő, de ezt a Programozás papíron részben feladókról szóló használati eset ............................................................................. 10

2.3. Köszönet .................................................................................................................. 112.4. Figyelmeztetés ......................................................................................................... 112.5. Könyvjelzők ............................................................................................................. 11

2.5.1. Szervezési könyvjelzők ............................................................................... 112.5.2. Főbb tartalmi könyvjelzők .......................................................................... 112.5.3. Technikai könyvjelzők ................................................................................ 12

3. Előzetes a példaprogramokból ............................................................................................. 123.1. Egygépes példák ...................................................................................................... 13

3.1.1. A LabirintusVilág példa .............................................................................. 133.1.2. A LabirintusApplet példa ............................................................................ 133.1.3. A LabirintusJáték példa ............................................................................... 14

3.2. Mobiltelefonos példák ............................................................................................. 143.2.1. LabirintusMIDlet példa ............................................................................... 14

3.3. Hálózati példák ........................................................................................................ 153.3.1. A LabirintusServlet példa ............................................................................ 153.3.2. A HálózatiLabirintus példa .......................................................................... 163.3.3. A TávoliLabirintus példa ............................................................................. 163.3.4. A KorbásLabirintus példa ............................................................................ 173.3.5. A ElosztottLabirintus példa ......................................................................... 17

3.4. További példák ......................................................................................................... 183.4.1. A további önálló példák könyvjelzői ........................................................... 193.4.2. A példák jellege ........................................................................................... 203.4.3. Összefoglalás .............................................................................................. 203.4.4. Platform ....................................................................................................... 213.4.5. A példaprogramok szerkezete, kipróbálása és a kézikönyv jelölései .......... 21

4. Látványos Ars Poetica .......................................................................................................... 25I. Programozás papíron ........................................................................................................................ 1

1. A programozásról .................................................................................................................... 21. A programozás filozófiája ............................................................................................. 2

1.1. A programozás alapjai ...................................................................................... 31.1.1. Algoritmikus kérdések ......................................................................... 31.1.2. A programozás evolúciója ................................................................. 491.1.3. A programozás egy filogenetikai törzsfája ........................................ 491.1.4. Bepillantás napjaink gyakorlatába ..................................................... 521.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 .................................................. 591.2.1. A Java platform .................................................................................. 61

Created by XMLmind XSL-FO Converter.

Javát tanítok

1.2.2. Objektumok mindenütt: a CORBA OO világ .................................... 721.3. Az internet, a programozó tágabb hazája ....................................................... 75

1.3.1. TCP/IP ............................................................................................... 751.3.2. A kliens-szerver modell ..................................................................... 761.3.3. Bepillantás az alkalmazási rétegbe: a HTTP protokoll ..................... 76

2. Saját világok teremtése és Java alapok ................................................................................ 801. A Java világa ............................................................................................................... 80

1.1. A Java nyelv ................................................................................................... 801.1.1. ........................................................................................................... 821.1.2. Osztályok és objektumok .................................................................. 831.1.3. A programozás világnyelve a Java .................................................... 851.1.4. Vissza az OO-hoz ............................................................................ 1031.1.5. Mi történik a metódusokban? .......................................................... 1151.1.6. Eseménykezelés .............................................................................. 1211.1.7. Kivételkezelés ................................................................................. 1241.1.8. Párhuzamos végrehajtás .................................................................. 1281.1.9. Interfészek ....................................................................................... 1291.1.10. Csomagok ...................................................................................... 130

1.2. A Java nyelv használata ............................................................................... 1301.2.1. Bepillantás a GUI programozásba .................................................. 1301.2.2. Bepillantás a hálózati programozásba ............................................. 1331.2.3. Esettanulmány: egy chat program ................................................... 134

II. Programozás gépen ..................................................................................................................... 1463. Java esettanulmányok ........................................................................................................ 147

1. Labirintus esettanulmányok Java nyelven ................................................................ 1471.1. A tiszta OO labirintus - Labirintus Világ ..................................................... 147

1.1.1. A labirintus API felélesztése ............................................................ 1471.1.2. A LabirintusVilág osztály ................................................................ 1641.1.3. A labirintus API és a LabirintusVilág a NetBeans IDE környezetben 169

1.2. Java a játékokban: egy teljes képernyős példa - Labirintus Játék ................ 1721.2.1. A LabirintusJáték osztály ................................................................ 1721.2.2. A teljes képernyős labirintus fordítása, futtatása ............................. 178

1.3. Java a böngészőkben: Applet objektumok - Labirintus Applet ................... 1791.3.1. A LabirintusApplet osztály .............................................................. 1791.3.2. A labirintus applet fordítása, futtatása ............................................. 183

1.4. Java a mobiltelefonokban: MIDlet objektumok - Labirintus MIDlet .......... 1841.4.1. A LabirintusMIDlet osztály ............................................................. 1891.4.2. A LabirintusVaszon osztály ............................................................. 191

1.5. Java a webszerverekben: Servlet objektumok - Labirintus Servlet ............. 1941.5.1. A LabirintusServlet osztály ............................................................. 198

1.6. Java a hálózaton ........................................................................................... 2011.6.1. TCP/IP - Hálózati Labirintus ........................................................... 2011.6.2. Java RMI - Távoli Labirintus ......................................................... 2101.6.3. CORBA - Korbás Labirintus ........................................................... 216

1.7. Elosztott objektumok - Elosztott labirintus ................................................. 2231.7.1. Az elosztott labirintus API felélesztése ........................................... 2231.7.2. Az Elosztott labirintus fordítása és futtatása ................................... 240

4. Példaprogramok ................................................................................................................. 2461. A csomagok szervezése ............................................................................................ 246

1.1. A példaprogramok forrásainak letöltése ...................................................... 2461.2. javattanitok.labirintus csomag ..................................................................... 246

Created by XMLmind XSL-FO Converter.

Javát tanítok

1.3. javattanitok.elosztott csomag ....................................................................... 2471.4. javattanitok csomag ..................................................................................... 247

1.4.1. A LabirintusAlkalmazás osztály ...................................................... 2485. A példák kipróbálása .......................................................................................................... 254

1. A Java telepítése gépünkre ....................................................................................... 2541.1. A Java SE, Java ME fejlesztői csomag letöltése és beállítása ..................... 254

1.1.1. Java SE Linux környezetben ........................................................... 2541.1.2. Java SE Windows környezetben ..................................................... 2551.1.3. A Java dokumentáció, azaz az API doksi letöltése .......................... 2551.1.4. Java ME ........................................................................................... 2561.1.5. A NetBeans integrált fejlesztői környezet letöltése és használata ... 2561.1.6. A NetBeans IDE 5.5 ........................................................................ 257

2. A példaprogramok futtatásáról általában .................................................................. 257A. Java mellékletek ................................................................................................................ 259

1. A Java verziók újdonságai a tigristől a delfinig ........................................................ 2591.1. A tigris .......................................................................................................... 2591.2. A Musztáng .................................................................................................. 2611.3. A delfin ......................................................................................................... 265

2. Az első Java tapasztalatok ........................................................................................ 2653. Egyszerű összehasonlítások ..................................................................................... 267

3.1. Egyszerű összehasonlítások a sebesség kérdésében .................................... 2673.1.1. A PiBBPBench Java osztály ............................................................ 2693.1.2. A pi_bbp_bench forrás .................................................................... 2713.1.3. A PiBBPBench C Sharp osztály ...................................................... 273

3.2. A Java és a C Sharp nyelvi szintű összehasonlítása ..................................... 2753.2.1. Az alapvető nyelvi elemek összehasonlítása ................................... 279

B. Számítási mellékletek ........................................................................................................ 2841. Biológiai témájú programok ..................................................................................... 284

1.1. Genomi, aminosav vagy akár tetszőleges szekvenciák összehasonlítása .... 2841.1.1. A Pontmátrix osztály ....................................................................... 2891.1.2. A Swinges felület építésének alapszabálya ..................................... 295

1.2. Sejtautomata szimuláció programja ............................................................. 2961.3. Orch OR demonstrációk .............................................................................. 309

1.3.1. Hexagonális rács ............................................................................. 3092. Matematikai témájú programok ............................................................................... 317

2.1. Galton deszka kísérlet programja ................................................................ 3172.2. Mandelbrot halmaz programja ..................................................................... 3222.3. Mandelbrot halmaz nagyító programja ........................................................ 3302.4. Mandelbrot halmaz pontjait grafikusan iteráló program ............................. 3452.5. A Mandelbrot halmazzal kapcsolatos osztályaink összefoglalása ............... 348

2.5.1. A MandelbrotHalmaz osztály .......................................................... 3492.5.2. A MandelbrotHalmazNagyító osztály ............................................. 3532.5.3. A MandelbrotIterációk osztály ........................................................ 356

2.6. A Pi jegyeinek nyomában ............................................................................ 358Irodalomjegyzék .............................................................................................................................. 362

Created by XMLmind XSL-FO Converter.

A táblázatok listája1.1. A bonyolultságmérő programok összefoglalása. ........................................................................ 141.2. A bonyolultságmérő programok és bemenetük együttes összefoglalása. ................................... 151.3. A 01-et ismétlő bináris sorozat kezdőrészeinek vizsgálata. ........................................................ 361.4. A félig véletlen bináris sorozat kezdőrészeinek vizsgálata. ........................................................ 391.5. Mikrotubulus sejtautomata szimuláció. ...................................................................................... 561.6. Java és C egyszerű sebesség összehasonlítása ............................................................................ 652.1. A Java primitív típusai .............................................................................................................. 115A.1. Java, gcj és C egyszerű sebesség összehasonlítása .................................................................. 269A.2. Java és C# egyszerű sebesség összehasonlítása ....................................................................... 269B.1. Az R. W. Gosper-féle sikló ágyú élőlény bemutatása .............................................................. 303

Created by XMLmind XSL-FO Converter.

A példák listája1.1. Első sorozat: 1000 nulla .............................................................................................................. 101.2. Első sorozat, máshogy: 1000 nulla ............................................................................................. 111.3. Második sorozat: 01010101 ... .................................................................................................... 111.4. Harmadik sorozat: 00000000001 ... 10000000000 ..................................................................... 121.5. Negyedik sorozat: egyre több 1 jegy két nulla között ................................................................ 121.6. Ötödik sorozat: pszeudo véletlen ................................................................................................ 131.7. Bármely sorozat: egy másoló program ....................................................................................... 141.8. A Chaitin-Kolmogorov bonyolultság nem kiszámítható! ........................................................... 161.9. A Chaitin-Kolmogorov bonyolultság gyakorlati alkalmazásai ................................................... 181.10. Átváltás binárisba ..................................................................................................................... 191.11. Bitműveletek, kezdjük egy bináris dumppal ............................................................................. 201.12. Titkosítás kizáró vaggyal .......................................................................................................... 211.13. Kizáró vagyos titkosítás grafikusan .......................................................................................... 231.14. Számrendszer átváltások ........................................................................................................... 241.15. A TörtÁtváltó osztály kiegészítése ............................................................................................ 251.16. Turing gépek kódolása .............................................................................................................. 251.17. Kongruencia generátorok .......................................................................................................... 261.18. Galton deszka kísérlet ............................................................................................................... 271.19. Hisztogram feladat .................................................................................................................... 311.20. Fej vagy írás varázslat ............................................................................................................... 341.21. Egy szabályos sorozat ............................................................................................................... 351.22. Véletlenszám generátorok ......................................................................................................... 371.23. Egy másik szabályos sorozat .................................................................................................... 381.24. Egy nem véletlen, de bonyolultabb sorozat .............................................................................. 381.25. A Chaitin-féle Omega konstans ismeretében meg tudnánk oldani a megállási problémát! ...... 411.26. A Pi közelítése ........................................................................................................................... 431.27. A Ramanujan és a Chudnovsky közelítő összegek formulái .................................................... 441.28. Az Ar klasszikus elsőrendű matematikai logikai nyelv ............................................................ 471.29. Saját terület formalizálása ......................................................................................................... 481.30. Mikrotubulus sejtautomata szimuláció ..................................................................................... 561.31. Java disassembler ...................................................................................................................... 631.32. Port szkennelő példa ................................................................................................................. 781.33. Jól ismert portok feladat ........................................................................................................... 792.1. Saját labirintusunk elképzelése ................................................................................................... 822.2. A RuntimeException típusú hibák kezeléséről 2. ..................................................................... 127A.1. Írjunk az OsztályNév osztályunkhoz egy példánymetódust, ami visszaadja az osztály String tagját! .......................................................................................................................................................... 282B.1. Pontmátrix osztály kiegészítése ............................................................................................... 293B.2. További kísérletek a Pontmátrix osztályunkkal ....................................................................... 294B.3. A Galton deszka kísérlet programjának kiegészítései .............................................................. 321B.4. A Mandelbrot halmaz nagyító programjának kiegészítései ..................................................... 343

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.

Created by XMLmind XSL-FO Converter.

Bevezetés

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.

Created by XMLmind XSL-FO Converter.

Bevezetés

Bátfai Norbert

2.1. BíztatásA 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 esetekAz 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

Created by XMLmind XSL-FO Converter.

Bevezetés

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

Created by XMLmind XSL-FO Converter.

Bevezetés

2.3. KöszönetEz 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ásA 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ők2.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

Created by XMLmind XSL-FO Converter.

Bevezetés

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.

Created by XMLmind XSL-FO Converter.

Bevezetés

MegjegyzésEbbe 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ákAz 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

Created by XMLmind XSL-FO Converter.

Bevezetés

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ákEzen 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

Created by XMLmind XSL-FO Converter.

Bevezetés

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ákA 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

Created by XMLmind XSL-FO Converter.

Bevezetés

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

Created by XMLmind XSL-FO Converter.

Bevezetés

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

Created by XMLmind XSL-FO Converter.

Bevezetés

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ákTalá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.

Created by XMLmind XSL-FO Converter.

Bevezetés

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.)

Created by XMLmind XSL-FO Converter.

Bevezetés

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 [358]. (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 = zn

2 + 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.

Created by XMLmind XSL-FO Converter.

Bevezetés

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áshozHa 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 rendszerMindazoná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 6A 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.

Created by XMLmind XSL-FO Converter.

Bevezetés

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ásaHa 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.

Created by XMLmind XSL-FO Converter.

Bevezetés

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.

Created by XMLmind XSL-FO Converter.

Bevezetés

[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ólA 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ólA 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ó ServerSocket osztályt a java.net csomagban találja meg. Az API dokumentáció telepítéséről A Java

Created by XMLmind XSL-FO Converter.

Bevezetés

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

Created by XMLmind XSL-FO Converter.

Bevezetés

Created by XMLmind XSL-FO Converter.

I. rész - Programozás papíronEbben 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.

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...

Created by XMLmind XSL-FO Converter.

A programozásról

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éneteA 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ő!

Created by XMLmind XSL-FO Converter.

A programozásról

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!

Created by XMLmind XSL-FO Converter.

A programozásról

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ő nullák, akkor azokat törölje le. Egyébként ugyanúgy kettővel szorozzon!

Created by XMLmind XSL-FO Converter.

A programozásról

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ő:

1.1.1.1.1. Az univerzális Turing gépek

Created by XMLmind XSL-FO Converter.

A programozásról

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

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.

Created by XMLmind XSL-FO Converter.

A programozásról

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:

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:

Created by XMLmind XSL-FO Converter.

A programozásról

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.

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

Created by XMLmind XSL-FO Converter.

A programozásról

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>intmain(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

intmain(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óknakEbben 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!

00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Created by XMLmind XSL-FO Converter.

A programozásról

00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

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:

010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101

Created by XMLmind XSL-FO Converter.

A programozásról

0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101

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:

0000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000000000

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"); }}

Ez a program 170 karakterből áll.

1.5. példa - Negyedik sorozat: egyre több 1 jegy két nulla között

Created by XMLmind XSL-FO Converter.

A programozásról

Tekintsük a negyedik 1000 darab karakterből álló mondatot, 0 számjegyek között egyre több 1 számjegy van:

0010110111011110111110111111011111110111111110111111111011111111110111111111110111111111111011111111111110111111111111110111111111111111011111111111111110111111111111111110111111111111111111011111111111111111110111111111111111111110111111111111111111111011111111111111111111110111111111111111111111110111111111111111111111111011111111111111111111111110111111111111111111111111110111111111111111111111111111011111111111111111111111111110111111111111111111111111111110111111111111111111111111111111011111111111111111111111111111110111111111111111111111111111111110111111111111111111111111111111111011111111111111111111111111111111110111111111111111111111111111111111110111111111111111111111111111111111111011111111111111111111111111111111111110111111111111111111111111111111111111110111111111111111111111111111111111111111011111111111111111111111111111111111111110111111111111111111111111111111111111111110111111111111111111111111111111111111111111011111111111111111111111111111111111111111111111111111

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:

0101101000001001000011100011111011110001010010011110100100000001010010001110101001111111010000001001000011011000101011110010011010100010010000010110011001000101011001100100011010011100001101011100111110110100011011100010100100000011100111100010100100111110010011110110111100011011101101001111000010100101000011111000010001110110011100101011000000011011111011011101101110110110000101010110011000100111001110101100011110100111101000000111101010111000011011000011110010010101010000000111111110110000010000100000100000001111100010101011111100000001000111110111010011010001011010011111010100111010100110101101011011110011001101011011011101101111001101110001001010111111111111110000010101111001101001010011011110110010001001001011101100011110111000011001001101010011011110101100101110101101111011010010001110001111100010100010111111100100010010111010000001110001101010101000001100100100100111100001101101110011111101011010111101011000110011000010110101000011111110100000100111011101010100101101001110010001

Ezt a sztringet nyomtatja ki az ÖtödikSorozat1 nevű program:

public class ÖtödikSorozat1 { public static void main(String[] args) {

Created by XMLmind XSL-FO Converter.

A programozásról

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!

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);

}

Created by XMLmind XSL-FO Converter.

A programozásról

}

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 0101101000001001000011100011111011110001010010011110100100000001010010001110101001111111010000001001000011011000101011110010011010100010010000010110011001000101011001100100011010011100001101011100111110110100011011100010100100000011100111100010100100111110010011110110111100011011101101001111000010100101000011111000010001110110011100101011000000011011111011011101101110110110000101010110011000100111001110101100011110100111101000000111101010111000011011000011110010010101010000000111111110110000010000100000100000001111100010101011111100000001000111110111010011010001011010011111010100111010100110101101011011110011001101011011011101101111001101110001001010111111111111110000010101111001101001010011011110110010001001001011101100011110111000011001001101010011011110101100101110101101111011010010001110001111100010100010111111100100010010111010000001110001101010101000001100100100100111100001101101110011111101011010111101011000110011000010110101000011111110100000100111011101010100101101001110010001|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

Harmadik sorozat HarmadikSorozat1 170 0 170

TetszőlegesSorozat1 147 1000 1147

Negyedik sorozat NegyedikSorozat1 177 0 177

TetszőlegesSorozat1 147 1000 1147

Created by XMLmind XSL-FO Converter.

A programozásról

SOROZAT PROGRAM KÓD MÉRET INPUT

MÉRET

A bonyolultság felső becslése

Ö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óknakA 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

Created by XMLmind XSL-FO Converter.

A programozásról

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; } }

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

Created by XMLmind XSL-FO Converter.

A programozásról

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:

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óknakA 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

Created by XMLmind XSL-FO Converter.

A programozásról

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 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 -2 31-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.txt110000110001110010110000111000101100011

Created by XMLmind XSL-FO Converter.

A programozásról

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 NullaEgy110000110001110010110000111000101100011

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 1100001 49 1100012 50 110010a 97 1100001b 98 1100010c 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óknakHa 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 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)

Created by XMLmind XSL-FO Converter.

A programozásról

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.class110010101111111010111010101111100000000000000000000000000011001000000000010000010000101000000000000100000000000000011101000010010000000000011110000000000001111100001010000000000010000000000000001000010000100100000000000111100000000000100010...

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!

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ő.

Created by XMLmind XSL-FO Converter.

A programozásról

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övegEz titkosítva lesz!Titkosítva, bizony![norbi@niobe ~]$ more titkosított.szöveg$LLk5AALk[norbi@niobe ~]$ java ExorTitkosító alma < titkosított.szövegEz titkosítva lesz!Titkosítva, bizony!

A Windows parancssorból dolgozóknakA Windows parancssorból dolgozó kedves Olvasó teljesen a Linux esetén mutatottak mintájára járhat el:

C:\...> javac ExorTitkosító.javaC:\...> java ExorTitkosító alma > titkosított.szövegEz titkos lesz!

C:\...> type titkosított.szöveg...olvashatatlan...LlkC:\...> java ExorTitkosító alma < titkosított.szövegEz 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ó.

Created by XMLmind XSL-FO Converter.

A programozásról

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(); } } }

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

Created by XMLmind XSL-FO Converter.

A programozásról

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]); 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ó.javaC:\...> java TörtÁtváltó0.05923938751220703

Created by XMLmind XSL-FO Converter.

A programozásról

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ó.javaC:\...> java TörtÁtváltó0.059239387512207030F2A5 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) {

Created by XMLmind XSL-FO Converter.

A programozásról

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

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);

Created by XMLmind XSL-FO Converter.

A programozásról

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.javaC:\...>java KongruenciaGenerátor6 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 50 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 21 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 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óknakHa 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.

Created by XMLmind XSL-FO Converter.

A programozásról

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?

Created by XMLmind XSL-FO Converter.

A programozásról

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;

Created by XMLmind XSL-FO Converter.

A programozásról

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.73024357453499510.3398973333782606-0.1745186408410782-0.6733291138289893-0.71412553337023770.8105205642319349-0.2166963741203095-0.6100935726625737-0.00612571585004756650.09213084665478943

Figyelmeztetés a Javaban kezdő Olvasóknak

Created by XMLmind XSL-FO Converter.

A programozásról

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. * * @param min a feldolgozandó értékek alsó határa. * @param max a feldolgozandó értékek felső határa.

Created by XMLmind XSL-FO Converter.

A programozásról

* @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)); else g.fillRect(i*dobozSzélesség,

Created by XMLmind XSL-FO Converter.

A programozásról

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álisC:\...> 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óknakAz iménti Hisztogram osztály betesz(double érték) módszerét módosítsuk úgy, hogy dobjon egy

Created by XMLmind XSL-FO Converter.

A programozásról

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:

11110010010010111101001110010001110011101100111100001100110011011101110001010011111100010010010011001010011101110010011100010000001110110100100001001111011000011111000101101010001001111010010011000010

S egy másik a fejből véletlennek generált/titulált alapon képzett sorozatra:

01110010110100110101000010110010110100101011110101100110010101011010010010001101010101001001011010011100110001100111101101111010001011001011101010010110000110101110110100001101010110111010101101010011

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, 2 8, 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 darab 989 bites = 2990 -1 darab összesen. A -1 bemenetet még nagylelkűen hozzácsapva, számoljunk 2 990

darabbal.

Created by XMLmind XSL-FO Converter.

A programozásról

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; } for(int i=0; i<db; ++i) System.out.print(i%2);

Created by XMLmind XSL-FO Converter.

A programozásról

}}

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 10

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 100101010101

A program 185 betű, az inputja 2 betű, összesen 187 betű. Az n=100 értékkel indítva:

[norbi@niobe ~]$ java BinárisSorozat 1000101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101

A program 185 betű, az inputja 3 betű, összesen 188 betű. Ezerrel indítva:

[norbi@niobe ~]$ java BinárisSorozat 10000101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101

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.

HOSSZ BONYOLULTSÁG A KETTŐ HÁNYADOSA

1 186 186

Created by XMLmind XSL-FO Converter.

A programozásról

HOSSZ BONYOLULTSÁG A KETTŐ HÁNYADOSA

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)); } }

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

Created by XMLmind XSL-FO Converter.

A programozásról

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 10

C:\...> java FéligVéletlen 2Az 1. tag: 1

Created by XMLmind XSL-FO Converter.

A programozásról

01

C:\...> java FéligVéletlen 3Az 1. tag: 1

010

C:\...> java FéligVéletlen 4Az 1. tag: 1Az 3. tag: 0

0100

C:\...> java FéligVéletlen 10Az 1. tag: 1Az 3. tag: 0Az 5. tag: 1Az 7. tag: 1Az 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. Egész természet átborzadna tőle. Szentelt pecsét az, feltartá az Úr

Created by XMLmind XSL-FO Converter.

A programozásról

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á:

C:\...> javac Program.java

Created by XMLmind XSL-FO Converter.

A programozásról

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

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

Created by XMLmind XSL-FO Converter.

A programozásról

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!

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.

Created by XMLmind XSL-FO Converter.

A programozásról

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 Pipi ~ Pi(n) = 3.0418396189294032, n = 10, Math.PI/Pi(n) = 1.0327936535639899pi ~ Pi(n) = 3.1315929035585537, n = 100, Math.PI/Pi(n) = 1.0031931832582315pi ~ Pi(n) = 3.140592653839794, n = 1000, Math.PI/Pi(n) = 1.0003184111600008pi ~ Pi(n) = 3.1414926535900345, n = 10000, Math.PI/Pi(n) = 1.0000318320017856pi ~ Pi(n) = 3.1415826535897198, n = 100000, Math.PI/Pi(n) = 1.0000031831090173pi ~ Pi(n) = 3.1415916535897743, n = 1000000, Math.PI/Pi(n) = 1.0000003183099935pi ~ Pi(n) = 3.1415925535897915, n = 10000000, Math.PI/Pi(n) = 1.00000003183099pi ~ Pi(n) = 3.141592643589326, n = 100000000, Math.PI/Pi(n) = 1.0000000031832477pi ~ Pi(n) = 3.1415926525880504, n = 1000000000, Math.PI/Pi(n) = 1.0000000003188645pi ~ Pi(n) = 3.141592653488346, n = 10000000000, Math.PI/Pi(n) = 1.0000000000322917pi ~ Pi(n) = 3.141592653578537, n = 100000000000, Math.PI/Pi(n) = 1.000000000003583

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

Created by XMLmind XSL-FO Converter.

A programozásról

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ábanHa 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 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

Created by XMLmind XSL-FO Converter.

A programozásról

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 RamanujanPipi ~ Pi(n) = 3.141592710907427, n = 2, Math.PI/Pi(n) = 0.9999999817552309pi ~ Pi(n) = 3.1415927109074255, n = 3, Math.PI/Pi(n) = 0.9999999817552313pi ~ 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 [303] című táblázatban, 1985-ben erre a formulára alapozva, 17.526.200 tizedesjegyig határozta meg a Pi értékét.

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.

Created by XMLmind XSL-FO Converter.

A programozásról

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 ChudnovskyPipi ~ Pi(n) = 3.141592653589764, n = 2, Math.PI/Pi(n) = 1.0000000000000093pi ~ Pi(n) = 3.141592653589764, n = 3, Math.PI/Pi(n) = 1.0000000000000093pi ~ 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 PiBBP6C65E52CB459350050E4BB178F4C67A0FCF7BF27206290FBE70F93B828CD939C475C728F2FDB0CB923CF52C40D631D4DB2E98340AA25A6F07DB685C0A9C04F3F6E667CFD6E1764C83ECA94E79661FC180E6AEF581987E79E13278712CB01255E8CE4D9E048F782D756370548FB0778323CF2074C2716D121639F1DD5A31EF6C242676B3783AD528852CCA52A9B4F999C526B0750859AEEC9CE6635B30996A210CD419D5FD47A4E7AAF906E26A4CCF99A2E493BBB5E7D5E0B94F15196DA8CD1A0C57FE03A629B2D5842317C173D163EA8717B46930EE0FE82FEC4B01016F155FB446AA6958EAD9265EC0C914CB84755DD1BCE5100C23804D67A787BEC57CD7D8E190B3F55E3D2558927215504F141AC8B0BA836F7781E19664EFA8B22BEB3816A70F7210E4784A1F37762361286448CD051BCE3A4CE156D70CDBA256C1A36C38648633C8F13A53405795635084A2DEAF3B9066BC3863BB07447DDDBDE5644034A6893E3E1CFDB369631BAA4240D93F17F667F7C51ABF076F7C1BB35DECC240153F4817A579CBD1DAC895E8555929D1ADA3C787A0BF2881BBC44C4BE505E91FE5A28B9BA47D4845B7639239AD71D8B63BF9D23B2CC88C9D39C

Created by XMLmind XSL-FO Converter.

A programozásról

033B0482F5F801D778BBB734EA8B1BE878D129514BFA5C4A6D60E80CF4B14A2A5673992B1839723054BD44F767B03245F2873973EF6D84B2B96EFC9A

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

• minden számnál van nagyobb: MINDENx (LÉTEZIKy(x < y))

• van olyan szám, aminél nincs nagyobb: LÉTEZIKy (MINDENx (x < y))

Created by XMLmind XSL-FO Converter.

A programozásról

• 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|

Created by XMLmind XSL-FO Converter.

A programozásról

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.”

Created by XMLmind XSL-FO Converter.

A programozásról

—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 maguknak. A [PROGRAMOZÓ PÁTERNOSZTER JEGYZET] jegyzetben arra láthatunk példát, hogyan

Created by XMLmind XSL-FO Converter.

A programozásról

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

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

Created by XMLmind XSL-FO Converter.

A programozásról

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.

Created by XMLmind XSL-FO Converter.

A programozásról

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].

Created by XMLmind XSL-FO Converter.

A programozásról

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].

Created by XMLmind XSL-FO Converter.

A programozásról

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

Az [ORCH OR] modell szerint a mikrotubulus tubulin dimerek alkotta hexagonális rácsa bizonyos

Created by XMLmind XSL-FO Converter.

A programozásról

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 10 9 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ó.

Created by XMLmind XSL-FO Converter.

A programozásról

Created by XMLmind XSL-FO Converter.

A programozásról

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 reguláris képzése nem vértezi fel a hallgatókat azokkal a matematikai ismeretekkel, amik e könyv élvezéséhez

Created by XMLmind XSL-FO Converter.

A programozásról

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 kihívásra kell válaszolniuk, hogy modellezni tudják-e a programozó előtt álló feladat világát? Ha igen, akkor a

Created by XMLmind XSL-FO Converter.

A programozásról

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 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ű

Created by XMLmind XSL-FO Converter.

A programozásról

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, például a futó operációs rendszerekkel: Linux-szal, Windows-zal vagy Solarisszal foglalkoznia kéne.

Created by XMLmind XSL-FO Converter.

A programozásról

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!

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

Created by XMLmind XSL-FO Converter.

A programozásról

(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 Fibonacci2 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!

TippHa már nagyobb gyakorlattal rendelkezünk, akkor a 0,1 feladat feladata is segíthet.

1.31. példa - Java disassembler

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

Created by XMLmind XSL-FO Converter.

A programozásról

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 FibonacciCompiled 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ésBá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

long delta = System.currentTimeMillis();

Created by XMLmind XSL-FO Converter.

A programozásról

/* 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 ~]$ ./fibonacci2 3 5 8 13 21 34 55 89 144

Lássuk, hogy valóban futtatható állomány készült

[norbi@niobe ~]$ file fibonaccifibonacci: 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:

[norbi@niobe ~]$ file Fibonacci.classFibonacci.class: compiled Java class data, version 50.0

Created by XMLmind XSL-FO Converter.

A programozásról

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.

Created by XMLmind XSL-FO Converter.

A programozásról

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; }

Created by XMLmind XSL-FO Converter.

A programozásról

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.

Created by XMLmind XSL-FO Converter.

A programozásról

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:

Created by XMLmind XSL-FO Converter.

A programozásról

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.Labirintusclass 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.

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ő

Created by XMLmind XSL-FO Converter.

A programozásról

ü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.

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

Created by XMLmind XSL-FO Converter.

A programozásról

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 é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.

Created by XMLmind XSL-FO Converter.

A programozásról

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

Created by XMLmind XSL-FO Converter.

A programozásról

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.

Created by XMLmind XSL-FO Converter.

A programozásról

*/ 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

Created by XMLmind XSL-FO Converter.

A programozásról

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/tcpftp 21/udp fsp fspdssh 22/tcp # SSH Remote Login Protocolssh 22/udp # SSH Remote Login Protocol...smtp 25/tcp mailsmtp 25/udp mail...http 80/tcp www www-http # WorldWideWeb HTTPhttp 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 statusA(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

Created by XMLmind XSL-FO Converter.

A programozásról

[root@niobe ~]# /etc/rc.d/init.d/httpd starthttpd 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 80Trying 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 80Trying 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 80Trying 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 OKDate: Sun, 10 Sep 2006 08:20:31 GMTServer: Apache/2.2.0 (Fedora)Last-Modified: Sun, 10 Sep 2006 08:17:16 GMTETag: "158540-6f-41d1511bd9b00"Accept-Ranges: bytesContent-Length: 111Connection: closeContent-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.

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:

Created by XMLmind XSL-FO Converter.

A programozásról

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óknakA 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"); } } }

A program végigzongorázza a parancssorában megkapott nevű gép 1024 alatti számú TCP kapuit: megpróbál

Created by XMLmind XSL-FO Converter.

A programozásró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 figyeli20 nem figyeli21 figyeli22 figyeli23 nem figyeli24 nem figyeli25 figyeli26 nem figyeli...79 nem figyeli80 figyeli81 nem figyeli82 nem figyeli...

Csak saját gépreFontos, 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?

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 nyelvMinden 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óknakAz 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>intmain (void){ int c; while ((c = getchar ()) != EOF)

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

putchar (c); 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 ~]$ ./masolqqwweerrtt

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ásolqqwweerrtt

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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!

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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.

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

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

pazarló, mert az int értéke nem csupán 0 vagy 1 lehet, hanem a -2 31 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 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

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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óknakA 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 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

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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.javajavattanitok\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.javajavattanitok\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

• 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:

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

/** 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.javajavattanitok\LabirintusVilág.java:83: JÁTÉK_MEGY_HŐS_RENDBEN has private accessin 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.javajavattanitok\LabirintusVilág.java:83: JÁTÉK_MEGY_HŐS_RENDBEN has protected accessin 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() { notify(); }

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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 symbolsymbol : class Labirintuslocation: class javattanitok.LabirintusVilág protected Labirintus labirintus; ^javattanitok\LabirintusVilág.java:33: cannot find symbolsymbol : class Hőslocation: class javattanitok.LabirintusVilág protected Hős hős; ^javattanitok\LabirintusVilág.java:47: cannot find symbolsymbol : class RosszLabirintusKivétellocation: 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

import java.util.List;import java.util.Iterator;

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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.javajavattanitok\LabirintusVilág.java:31: cannot access javattanitok.labirintus.Labirintusbad class file: .\javattanitok\labirintus\Labirintus.javafile does not contain class javattanitok.labirintus.LabirintusPlease 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:

/* * Hős.java * * DIGIT 2005, Javat tanítok * Bátfai Norbert, [email protected] * */

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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ő

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()

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

{ 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 [104] 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(); } }

• return jelentése: visszatérés függvényből.

public class Kincs extends Szereplő { /** A kincs értéke. */ protected int érték;... /**

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

* 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': 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.

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

• 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) { // 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(); } });

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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:

/** * 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 -

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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.

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();

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

}

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.

Figyelmeztetés a Javaban kezdő OlvasóknakA 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

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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();

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();

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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; } }

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.

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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); }

• 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.

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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// 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 ... // .// .// .6310100 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

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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

/** * 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(); } }

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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!

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,

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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

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:

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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 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);

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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) { 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ó

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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.javaC:\...> java Labirintus

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.javaC:\...> java Labirintus10

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);

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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.javaLabirintus.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:

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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; }

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

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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:

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?

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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.

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();

}

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

}

1.1.4.2. Objektumok kommunikációja: a metódushívás

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

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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.

É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.

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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 {

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

/** 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

boolean false true

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

Típus Minimum Maximum

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) {

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

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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:

//// labirintus.txt//// DIGIT 2005, Javat tanítok// Bátfai Norbert, [email protected]

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

//// 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 ... // .// .// .6310100 0 0 1 0 1 0 1 1 10 0 0 0 0 0 0 0 0 01 0 1 0 1 0 1 0 1 00 0 0 0 1 0 1 0 0 00 1 1 0 0 0 1 1 0 10 0 0 0 1 0 0 0 0 00 1 0 0 0 1 0 1 1 00 0 0 1 0 1 0 1 0 00 1 0 0 0 0 0 0 0 10 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();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

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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;

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!

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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.javaC:\...> java TéglaA tegla tartalma most QWERTException in thread "main" java.lang.NullPointerException at Tégla.main(Tégla.java:12)

Olvassuk el a hibaüzeneteketAká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;

if(tegla != null) if(tegla.equals("QWERT")) System.out.println("A tegla tartalma most QWERT"); } }

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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);

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 {

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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(); 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;

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

// 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) { // 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:

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

// 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 {

}

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

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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

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();

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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 {

// 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) {

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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); 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.

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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

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.

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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 {

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.

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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álataA 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 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.

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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); // 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);

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

// 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.

A kódolt szöveget visszamásoltuk a tiszta szöveg dobozába, majd újra kódolva dekódoltuk.

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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=64Válasz 192.168.1.1: bájt=32 idő<10 ezredmp. TTL=64Válasz 192.168.1.1: bájt=32 idő<10 ezredmp. TTL=64Vá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

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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óknakA 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, 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.

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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); }

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 */

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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(); } /** * 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

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

// 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) { 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. */

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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.javaC:\Documents and Settings\norbi> java CsevegőSzerver

Egy másik ablakból a telnet localhost 2006 parancs kiadásával csatlakozunk a szerverhez:

C:\Documents and Settings\norbi> telnet localhost 2006egyik vagyokegyik vagyokUdv egyik, en a masik vagyokSzia masikSzia 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 2006egyik vagyokUdv egyik, en a masik vagyokUdv egyik, en a masik vagyok

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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 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:

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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 * @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) {

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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); * }; * * 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. */

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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"); // 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

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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 * @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);

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

// 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); } 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.idlC:\... > javac KorbaCsevegő.java KorbaCsevegőKliens.java korba\*.javaNote: Some input files use unchecked or unsafe operations.Note: Recompile with -Xlint:unchecked for details.

Indítjuk az ORB szoftvert:

Created by XMLmind XSL-FO Converter.

Saját világok teremtése és Java alapok

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őKliensVan 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 ErikaMatyi> 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 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.)

Created by XMLmind XSL-FO Converter.

II. rész - Programozás gépenEbben 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ó.

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 nyelvenA 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ágA 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 javattanitokC:\...\Munkakönyvtár> mkdir javattanitok\labirintus

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

// 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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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.

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

/* * 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. *

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* @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.

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* @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]

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* @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; } /**

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* 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ű

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* {@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;

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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("//"))

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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) {} }

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

} // 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;

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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. */

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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. */

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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("| "); }

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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;

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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;

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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); } }}

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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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.javaC:\...\Munkakönyvtár>java javattanitok.LabirintusVilágIndí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 ... // .// .// .6310100 0 0 1 0 1 0 1 1 10 0 0 0 0 0 0 0 0 01 0 1 0 1 0 1 0 1 00 0 0 0 1 0 1 0 0 00 1 1 0 0 0 1 1 0 10 0 0 0 1 0 0 0 0 00 1 0 0 0 1 0 1 1 00 0 0 1 0 1 0 1 0 00 1 0 0 0 0 0 0 0 10 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| | | | | | | | | | |FAL| |FAL| |FAL| |FAL| |FAL| | | | | |FAL| |FAL| | |

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

| |FAL|FAL| | | |FAL|FAL| |FAL| | | | |FAL| | | | | | |FAL| | | |FAL| |FAL|FAL| | | | |FAL| |FAL| |FAL| | | |FAL| | | | | | | |FAL| | | | |FAL| | | |FAL|FALA 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|FALA 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|FALA 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|FALA 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|FALA 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|FALA 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| | | | |FAL| |FAL| |FAL| | | |FAL| | | | | | | |FAL| | | |H |FAL| | | |FAL|FAL

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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|FALA 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|FALA 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|FALMegtalált értékek: 0Játékidő: 10 lépésHá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.

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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.

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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:

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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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ékA 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 * keltésére ad példát ez az osztály. * * @author Bátfai Norbert, [email protected]

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* @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(); if(!játékVége) switch(billentyű) { // hős mozgatása

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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. */ synchronized public void rajzolniKell() {

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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; } /** * A játék grafikus felületének aktív renderelése. */ public void rajzol() {

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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) g.drawString(végeÜzenet, 420, 350); g.dispose();

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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(); } /** * Átveszi a játék indításához szükséges paramétereket, majd

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* 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.javaC:\...\Munkakönyvtár> java javattanitok.LabirintusJáték labirintus.txt

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

1.3. Java a böngészőkben: Applet objektumok - Labirintus AppletA 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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* @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);

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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. */

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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); } }

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

/** * 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 --><object classid = "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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:

1.4. Java a mobiltelefonokban: MIDlet objektumok - Labirintus

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

MIDletAz 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:

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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:

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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!

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

A Launch-nek megfelelő telefon szoftbillentyűre kattintva elindul a mobil labirintus játékunk.

Még a játék képeit tartalmazó png állományokat kell megadnunk a projektnek, másoljuk be a hos.png,

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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 * @see javattanitok.LabirintusServlet

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* @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();

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

} } }}

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. */ private Thread játékSzál; /** A játékbeli idő mérésére.*/

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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) { hős.lépFöl(); } else if ((billentyű & DOWN_PRESSED) != 0) {

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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, téglaSzélesség/2, téglaMagasság); // A hős kirajzolása

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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 ServletAz 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 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.

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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.

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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.

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

A megfelelő linkek választásával a böngészőben akár győzhetünk is!

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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);

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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>");

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

// é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ózatonA 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] * */

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

package javattanitok;

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();

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

// 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 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.

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* * @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); }

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

try { 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 {

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

/** TCP-s kommunikációs kapcsolat a játékossal. */ 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; }

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

játékostól = bejövőCsatorna.readLine(); } 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); }

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

/** * 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.javaNote: 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.txtHősök száma: 0Hősök száma: 0Hősök száma: 0...

A szerver futtattása után egy másik ablakban (vagy akár egy másik gépen) futtassuk a klienst, ami most

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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 2006Trying 127.0.0.1...Connected to localhost.localdomain (127.0.0.1).Escape character is '^]'.A hős neve?MatyiParancsok: 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: 1Hősök száma: 1Hő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

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.

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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. * @exception RosszLabirintusKivétel ha a labirintust definiáló állomány * nem a megfelelő szerkezetű */

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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); } /** * Az RMI-n keresztül jelenetkező hős lépése.

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* * @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); } }}

1.6.2.2. A TávoliHősíthető osztály

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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;

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.

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* * @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.javaC:\...\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.

C:\...\Munkakönyvtár> java javattanitok.TávoliLabirintus labirintus.txtHősök száma: 0Hősök száma: 0Hősök száma: 0

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

...

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| | || |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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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ásajava.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ásaorg.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éseorg.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őreorg.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.

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ésecorbaObjektum =

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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ásaMint 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: 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,

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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); } }}

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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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)); } catch (Exception e) { e.printStackTrace(); } } }

1.6.3.3. A TavoliHos interfész

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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); * }; * }; * }; * </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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* @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); } /** * 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();

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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.javaNote: 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.txtHősök száma: 0Hősök száma: 0Hősök száma: 0...

és a kliens oldalt:

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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

| | |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 labirintusAz Előzetes a példaprogramokból című pontban bevezetett esettanulmány végleges kódját adjuk meg itt.

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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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; * 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); * }; * }; * };

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* }; * </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; } /** * 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; }}

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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 * { * 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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* </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. */ 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) {

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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 * { * 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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* </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."); } 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;

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

}}

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 * { * 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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* @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 * { * 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);

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* 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 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.

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* @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)); 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();

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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; } /** * 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. *

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

* @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) 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("| ");

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

} 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; 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); }; }; };};

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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); // 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);

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

// 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ó * @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". */

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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)); 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));

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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); }}

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.idlC:\...\Munkakönyvtár> javac javattanitok\*.java javattanitok\labirintus\*.java

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

javattanitok\elosztott\*.javaNote: 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épenHa 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.ElosztottKliensSZEREPLŐ> 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

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

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örnySZEREPLŐ> 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:0LABIRINTUS> 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ősHŐS> ELkészültem: IOR:000000000000002e49444c3a6a6176617474616e69746f6b2f656c6f737a746f74742f6f626a656b74756d6f6b2f486f733a312e30000000000000010000000000000086000102000000000e3137322e31372e3133352e3631000d7800000031afabcb0000000020ffe655a100000001000000000000000100000008526f6f74504f410000000008000000010000000014000000000000020000000100000020000000000001000100000002050100010001002000010109000000010001010000000026000000020002SZEREPLŐ> Beállították a pozíciómat. Idő:68 hős:1 kincs:1 szörny:1SZEREPLŐ> Lekérdezték a pozíciómat.SZEREPLŐ> Lekérdezték a pozíciómat.

Interoperable Object ReferenceCsupá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:1LABIRINTUS> Belépett egy hős: IOR:000000000000002e49444c3a6a6176617474616e69746f6b2f656c6f737a746f74742f6f626a656b74756d6f6b2f486f733a312e30000000000000010000000000000086000102000000000e3137322e31372e3133352e3631000d7800000031afabcb0000000020ffe655a100000001000000000000000100000008526f6f74504f410000000008000000010000000014000000000000020000000100000020000000000001000100000002050100010001002000010

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

109000000010001010000000026000000020002 Idő:68 hős:1 kincs:1 szörny:1 Idő:69 hős:1 kincs:1 szörny:1 ...

Interoperable Object ReferenceNem 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.idlC:\...\Munkakönyvtár> javac javattanitok\*.java java ttanitok\labirintus\*.java javattanitok\elosztott\*.javaNote: Some input files use unchecked or unsafe operations.Note: Recompile with -Xlint:unchecked for details.

Majd most ebből az ablakból is indítunk egy kincset.

C:\...\Munkakönyvtár> java javattanitok.ElosztottKliensSZEREPLŐ> Beállították a pozíciómat.KINCS> Nem talált rám. Idő:293 hős:1 kincs:2 szörny:1KINCS> 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.

...

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

Idő:291 hős:1 kincs:1 szörny:1 Idő:292 hős:1 kincs:1 szörny:1LABIRINTUS> 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örnySZEREPLŐ> 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:2SZÖ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:1LABIRINTUS> 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ősHŐS> ELkészültem: IOR:000000000000002e49444c3a6a6176617474616e69746f6b2f656c6f737a746f74742f6f626a656b74756d6f6b2f486f733a312e30000000000000010000000000000086000102000000000e3137322e31372e3133352e353300055d00000031afabcb0000000020ffebeff400000001000000000000000100000008526f6f74504f410000000008000000010000000014000000000000020000000100000020000000000001000100000002050100010001002000010109000000010001010000000026000000020002SZEREPLŐ> Beállították a pozíciómat. Idő:359 hős:2 kincs:2 szörny:2SZEREPLŐ> Lekérdezték a pozíciómat....

Interoperable Object ReferenceA 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:2LABIRINTUS> Belépett egy hős: .IOR:000000000000002e49444c3a6a6176617474616e69746

Created by XMLmind XSL-FO Converter.

Java esettanulmányok

f6b2f656c6f737a746f74742f6f626a656b74756d6f6b2f486f733a312e30000000000000010000000000000086000102000000000e3137322e31372e3133352e353300055d00000031afabcb0000000020ffebeff400000001000000000000000100000008526f6f74504f410000000008000000010000000014000000000000020000000100000020000000000001000100000002050100010001002000010109000000010001010000000026000000020002 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:2LABIRINTUS> 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.

Created by XMLmind XSL-FO Converter.

4. fejezet - Példaprogramok1. A csomagok szervezéseJelen 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éseA 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 csomagEgy 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

Created by XMLmind XSL-FO Converter.

Példaprogramok

1.3. javattanitok.elosztott csomagEgy 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 csomagA 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 javax.accessibility.Accessible)

Created by XMLmind XSL-FO Converter.

Példaprogramok

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 implements java.awt.event.KeyListener {

Created by XMLmind XSL-FO Converter.

Példaprogramok

/** 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(); } }); } // amit be is lehet csukni

Created by XMLmind XSL-FO Converter.

Példaprogramok

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 * állapot változásait is innen irányítjuk.

Created by XMLmind XSL-FO Converter.

Példaprogramok

*/ 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), j*falKép.getHeight(this), null);

Created by XMLmind XSL-FO Converter.

Példaprogramok

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. */ public static void main(String[] args) {

Created by XMLmind XSL-FO Converter.

Példaprogramok

// 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 helyzetbenA 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ő.

Created by XMLmind XSL-FO Converter.

5. fejezet - A példák kipróbálása1. A Java telepítése gépünkre1.1. A Java SE, Java ME fejlesztői csomag letöltése és beállításaMindig 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 -ltotal 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 -versionjava 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)

Created by XMLmind XSL-FO Converter.

A példák kipróbálása

igen, ez már a mi frissen feltett 6-os Javank.

Java kényelmesenHa 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 -versionjava 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 6A 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

Created by XMLmind XSL-FO Converter.

A példák kipróbálása

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.

Created by XMLmind XSL-FO Converter.

A példák kipróbálása

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ábanA 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 alattKé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:

Created by XMLmind XSL-FO Converter.

A példák kipróbálása

$ javac -encoding ISO-8859-2 javattanitok/LabirintusAlkalmazás.java

Created by XMLmind XSL-FO Converter.

A. függelék - Java mellékletek1. A Java verziók újdonságai a tigristől a delfinigMost 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 tigrisA 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:

Created by XMLmind XSL-FO Converter.

Java mellékletek

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;}

módszerét felüldefiniálja:

Created by XMLmind XSL-FO Converter.

Java mellékletek

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ángAzaz 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ületif (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(); }

Created by XMLmind XSL-FO Converter.

Java mellékletek

}); }

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özjava.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épif(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);

Created by XMLmind XSL-FO Converter.

Java mellékletek

// é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.

Created by XMLmind XSL-FO Converter.

Java mellékletek

Created by XMLmind XSL-FO Converter.

Java mellékletek

Illetve ugyancsak Linuxon a GNOME felületet használva az indítóikont.

1.3. A delfinA 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 tapasztalatokEbben 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 =

Created by XMLmind XSL-FO Converter.

Java mellékletek

new java.net.ServerSocket(2006); 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 KliensHelló, Világ![norbi@omega Munkakönyvtár]$ java KliensHelló, Világ![norbi@omega Munkakönyvtár]$ java KliensHelló, Világ!

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ű

Created by XMLmind XSL-FO Converter.

Java mellékletek

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ások3.1. Egyszerű összehasonlítások a sebesség kérdésébenA 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

Created by XMLmind XSL-FO Converter.

Java mellékletek

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) 10 6., 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_bench64.390000[norbi@niobe ~]$ ./pibbpbench66.386[norbi@niobe ~]$ java PiBBPBench64.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_bench751.190000[norbi@niobe ~]$ ./pibbpbench774.503[norbi@niobe ~]$ java PiBBPBench749.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_bench12586.000000[norbi@niobe ~]$ ./pibbpbench12854.993[norbi@niobe ~]$ java PiBBPBench12556.935

Created by XMLmind XSL-FO Converter.

Java mellékletek

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.csC:\...> javac PiBBPBench.javaC:\...> PiBBPBench610,65625C:\...> java PiBBPBench612.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.csC:\...> javac PiBBPBench.javaC:\...> PiBBPBench7125,0625C:\...> java PiBBPBench7147.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.

/* * PiBBPBench.java * * DIGIT 2005, Javat tanítok

Created by XMLmind XSL-FO Converter.

Java mellékletek

* 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; } return r; } /**

Created by XMLmind XSL-FO Converter.

Java mellékletek

* 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 * BBP Algorithm for Pi. alapján. */longn16modk (int n, int k)

Created by XMLmind XSL-FO Converter.

Java mellékletek

{ 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. */doubled16Sj (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;

clock_t delta = clock ();

for (d = 1000000; d < 1000001; ++d) {

Created by XMLmind XSL-FO Converter.

Java mellékletek

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) d16Sj += System.Math.pow(16.0d, d-k) / (double)(8*k + j); */ return d16Sj - System.Math.Floor(d16Sj);

Created by XMLmind XSL-FO Converter.

Java mellékletek

} /// <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); jegy = (int)System.Math.Floor(16.0d*d16Pi); }

Created by XMLmind XSL-FO Converter.

Java mellékletek

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ásaAz 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.javaC:\...> java HellóVilágHelló 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!");

}

}

Created by XMLmind XSL-FO Converter.

Java mellékletek

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.csC:\...> HellóVilág.exeHelló 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óknakA 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 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

Created by XMLmind XSL-FO Converter.

Java mellékletek

} 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) { 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);

Created by XMLmind XSL-FO Converter.

Java mellékletek

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 {

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 {

Created by XMLmind XSL-FO Converter.

Java mellékletek

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); 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

Created by XMLmind XSL-FO Converter.

Java mellékletek

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 {

}

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) { } }

Created by XMLmind XSL-FO Converter.

Java mellékletek

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!

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);

Created by XMLmind XSL-FO Converter.

Java mellékletek

} }

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.javaC:\...> java OsztályNévHelloHello

Adjuk meg a megoldást C Sharp nyelven is:

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; }

Created by XMLmind XSL-FO Converter.

Java mellékletek

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.javaC:\...> Osztály.exeHelloHello

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.

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ú programok1.1. Genomi, aminosav vagy akár tetszőleges szekvenciák összehasonlításaEbben 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.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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());

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

// 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:

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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] * */

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

/** * 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.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

// 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ü)) {

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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());

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

} 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);

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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;

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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.javaC:\...> 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.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

1.2. Sejtautomata szimuláció programjaEgy 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ő

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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) {

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

// 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

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

* 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Ő;

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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.javaC:\...> 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.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

• 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

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

1.3. Orch OR demonstrációkA 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ű.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

/* * 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];

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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) {

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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)

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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ó; }

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

} 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(); }

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

} 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.javaC:\...> 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ú programok2.1. Galton deszka kísérlet programjaMagá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; // Véletlenszám generátor

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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) g.setColor(java.awt.Color.WHITE);

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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) { // Melyik sorban van éppen az eső golyó?

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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.javaC:\...> 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.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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

g.drawString("golyók száma" + golyóSzámláló, 20, 100);

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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 programjaAz 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.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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) {

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

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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 z 2 = 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);

A Mandelbrot halmazt kiszámoló, kirajzoló programunk teljes kódja a következő.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

/* * 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) pillanatfelvétel(); // Az 'n' gomb benyomásával pontosabb számítást végzünk.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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("_"); // A fájl nevébe belevesszük, hogy melyik tartományban // találtuk a halmazt:

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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: rgb = (255-iteráció)| ((255-iteráció) << 8) |

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

((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.javaC:\...> 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.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

2.3. Mandelbrot halmaz nagyító programjaAz 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; /**

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

* 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);

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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ó.javaC:\...> java MandelbrotHalmazNagyító

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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(); }

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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);

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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;

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

// 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.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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); }

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

• 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); }

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

2.4. Mandelbrot halmaz pontjait grafikusan iteráló programAz 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 = zn

2 + c iteráció lépéseit: kirajzoljuk a komplex síkra az iteráció bejárta z 0, 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) {

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

// 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;

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

// 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) {} } }

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.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

2.5. A Mandelbrot halmazzal kapcsolatos osztályaink összefoglalásaA 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ó.javaC:\...> 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.

• 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

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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 */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;

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

/** 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: számításFut = true; new Thread(MandelbrotHalmaz.this).start(); } }

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

} }); // 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. * 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.

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

*/ 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; } /** Az aktuális Mandelbrot halmaz [a,b]x[c,d] adatai. * @return double c */ public double getC() { return c;

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

} /** 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. * @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) {

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

// 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 = 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);

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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; } /** * 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]

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

// 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. */ // 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;

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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éseKé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.javaC:\...\Munkakönyvtár> jar cmf manifest.mf mandelbrotos.jar *.class

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.)

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

2.6. A Pi jegyeinek nyomábanEz 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(); Character hexaJegyek[] = {'A', 'B', 'C', 'D', 'E', 'F'}; while(d16Pi != 0.0d) { int jegy = (int)StrictMath.floor(16.0d*d16Pi);

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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; } /** * 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() {

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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 PiBBP6C65E5308

(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 PiBBP6C65E52CB459350050E4BB178F4C67A0FCF7BF27206290FBE70F93B828CD939C475C728F2FDB0CB923CF52C40D631D4DB2E98340AA25A6F07DB685C0A9C04F3F6E667CFD6E1764C83ECA94E79661FC180E6AEF581987E79E13278712CB01255E8CE4D9E048F782D756370548FB0778323CF2074C2716D121639F1DD5A31EF6C242676B3783AD528852CCA52A9B4F999C526B0750859AEEC9CE6635B30996A210CD419D5FD47A4E7AAF906E26A4CCF99A2E493BBB5E7D5E0B94F15196DA8CD1A0C57FE03A629B2D5842317C173D163EA8717B46930EE0FE82FEC4B01016F155FB446AA6958EAD9265EC0C914CB84755DD1BCE5100C23804D67A787BEC57CD7D8E190B3F55E3D2558927215504F141AC8B0BA836F7781E19664EFA8B22BEB3816A70F7210E4784A1F37762361286448CD051BCE3A4CE156D70CDBA256C1A36C38648633C8F13A53405795635084A2DEAF3B9066BC3863BB07447DDDBDE5644034A6893E3E1CFDB369631BAA4240D93F17F667F7C51ABF076F7C1BB35DECC240153F4817A579CBD1DAC895E8555929D1ADA3C787A0BF2881BBC44C4BE505E91FE5A28B9BA47D4845B7639239AD71D8B63BF9D23B2CC88C9D39C033B0482F5F801D778BBB734EA8B1BE878D129514BFA5C4A6D60E80CF4B14A2A5673992B1839723054BD44F767B03245F2873973EF6D84B2B96EFC9A

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[]) {

Created by XMLmind XSL-FO Converter.

Számítási mellékletek

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 PiBBP243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89452821E638D01377BE5466CF34E90C6CC0AC29B7C97C50DD3F84D5B5B54709179216D5D98979FB1BD1310BA698DFB5AC2FFD72DBD01ADFB7B8E1AFED6A267E96BA7C9045F12C7F9924A19947B3916CF70801F2E2858EFC16636920D871574E69A458FEA3F4933D7E0D95748F728EB658718BCD5882154AEE7B54A41DC25A59B59C30D5392AF26013C5D1B023286085F0CA417918B8DB38EF8E79DCB0603A180E6C9E0E8BB01E8A3ED71577C1BD314B2778AF2FDA55605C60E65525F3AA55AB945748986263E8144055CA396A2AAB10B6B4CC5C341141E8CEA15486AF7C72E993B3EE1411636FBC2A2BA9C55D741831F6CE5C3E169B87931EAFD6BA336C24CF5C7A325381289586773B8F48986B4BB9AFC4BFE81B6628219361D809CCFB21A991487CAC605DEC8032EF845D5DE98575B1DC262302EB651B8823893E81D396ACC50F6D6FF383F442392E0B4482A484200469C8F04A9E1F9B5E21C66842F6E96C9A670C9C61ABD388F06A51A0D2D8542F68960FA728AB5133A36EEF0B6C137A3BE4BA3BF0507EFB2A98A1F1651D39AF017666CA593E82430E888CEE8619456F9FB47D84A5C33B8B5EBEE06F75D885C12073401A449F56C16AA64ED3AA62363F77061BFEDF72429B023D37D0D724D00A1248DB0FEAD349F1C09B075372C980991B7B25D479D8F6E8DEF7E3FE501AB6794C3B976CE0BD04C006BAC1A94FB6409F60C45E5C9EC2196A246368FB6FAF3E6C53B51339B2EB3B52EC6F6DFC511F9B30952CCC814544AF5EBD09BEE3D004DE334AFD660F2807192E4BB3C0CBA85745C8740FD20B5F39B9D3FBDB5579C0BD1A60320AD6A100C6402C7279679F25FEFB1FA3CC8EA5E9F8DB3222F83C7516DFFD616B152F501EC8AD0552AB323DB5FAFD23876053317B483E00DF829E5C57BBCA6F8CA01A87562EDF1769DBD542A8F6287EFFC3AC6732C68C4F5573695B27B0BBCA58C8E1FFA35DB8F011A010FA3D98FD2183B84AFCB56C2DD1D35B9A53E479B6F84565D28E49BC4BFB9790E1DDF2DAA4CB7E3362FB1341CEE4C6E8EF20CADA36774C01D07E9EFE2BF11FB495DBDA4DAE909198EAAD8E716B93D5A0D08ED1D0AFC725E08E3C5B2F8E7594B78FF6E2FBF2122B648888B812900DF01C4FAD5EA0688FC31CD1CFF191B3A8C1AD2F2F2218BE0E1777EA752DFE8B021FA1E5A0CC0FB56F74E818ACF3D6CE89E299B4A84FE0FD13E0B77CC43B81D2ADA8D9165FA2668095770593CC7314211A1477E6AD206577B5FA86C75442F5FB9D35CFEBCDAF0C7B3E89A0D6411BD3AE1E7E4900250E2D2071B35E226800BB57B8E0AF2464369BF009B91E5563911D59DFA6AA78C14389D95A537F207D5BA202E5B9C5832603766295CFA911C819684E734A41B3472DCA7B14A94A1B5100529A532915D60F573FBC9BC6E42B60A47681E6740008BA6FB5571BE91FF296EC6B2A0DD915B6636521E7B9F9B6FF34052EC585566453B02D5DA99F8FA108BA47996E85076A4B7A70E9B5B32944DB75092EC4192623AD6EA6B049A7DF7D9CEE60B88FEDB266ECAA8C71699A17FF5664526CC2B19EE1193602A575094C29A0591340E4183A3E3F54989A5B429D656B8FE4D699F73FD6A1D29C07EFE830F54D2D38E6F0255DC14CDD20868470EB266382E9C6021ECC5E09686B3F3EBAEFC93C9718146B6A70A1687F358452A0E286B79C5305AA5007373E07841C7FDEAE5C8E7D44EC5716F2B8B03ADA37F0500C0DF01C1F040200B3FFAE0CF51A3CB574B225837A58DC0921BDD19113F97CA92FF69432477322F547013AE5E58137C2DADCC8B576349AF3DDA7A94461460FD0030EECC8C73EA4751E41E238CD993BEA0E2F3280BBA1183EB3314E548B384F6DB9086F420D03F60A04BF2CB8129024977C795679B072BCAF89AFDE9A771FD9930810B38BAE12DCCF3F2E5512721F2E6B7124501ADDE69F84CD877A5847187408DA17BC9F9ABCE94B7D8CEC7AEC3ADB851DFA63094366C464C3D2EF1C18473215D908DD433B3724C2BA1612A14D432A65C45150940002133AE4DD71DFF89E10314E5581AC77D65F11199B043556F1

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ó.

Created by XMLmind XSL-FO Converter.

IrodalomjegyzékIdé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.

Created by XMLmind XSL-FO Converter.

Irodalomjegyzék

[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

Created by XMLmind XSL-FO Converter.

Irodalomjegyzék

[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.

Created by XMLmind XSL-FO Converter.

Irodalomjegyzék

[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 .

Created by XMLmind XSL-FO Converter.

Irodalomjegyzék

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 .

Created by XMLmind XSL-FO Converter.

Irodalomjegyzék

[HUMÁN GENOM PROJEKT] Humán Genom Projekt. http://www.ncbi.nlm.nih.gov/genome/seq .

[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 .

Created by XMLmind XSL-FO Converter.

Irodalomjegyzék

[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 .

[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 .

Created by XMLmind XSL-FO Converter.