Bevezetes a Java Programozasba

61
Bevezetés a programozásba a Jáva nyelven keresztül Szerzõk: Paller Gábor, Páskuj Attila Az olvasó most egy merész kísérlettel fog találkozni. Ebben a cikksorozatban megpróbálkozunk azzal, hogy egy általános reál tudással rendelkezõ olvasót beavassunk a számítógépprogramozásba és erre eszközül a Jáva nyelvet használjuk fel. A szokásos módszer az lenne, hogy a tanuló elõbb ismerje meg az algoritmikus alapokat és legalább egy funkcionális nyelvet, majd ezután vágjon bele egy olyan relatí ve bonyolult rendszer megismerésébe, mint a Jáva. Ezzel a módszerrel én egyetértek, ugyanakkor az elmúlt évben sok olyan panaszt hallottam, miszerint a "belépési szint" a Jávába túl magas, a tankönyvek megértéséhez az objektumorientált programozási paradigmát már ismerni kell. Nem mindenkinek van kedve, ideje vagy lehetõsége azonban végigjárnia az alapos tanuló útját és a tapasztalat azt mutatta, hogy ilyen esetekben is jó eredmények érhetõk el megfelelõen adaptált és fókuszált tananyaggal. A kezdõ tudás ugyan nem lesz olyan mély és a tanuló algoritmikus tudása is gyengébb lesz hagyományos képzést végzetteknél, azonban gyakorlattal sok minden pótolható és igen sok programozási feladatnál nincs szükség komplex algoritmusok kifejlesztésére vagy kódolására. Természetesen a fenti, redukált cél egyben intés is a diáknak: ne gondolja, hogy azért mert végigcsinálta ezt a leckesorozatot, azonnal képzett programozó lett belõle. Az algoritmikus alapok bõvítésére a késõbbiekben nagy szükség lesz és ez az anyag csak a kezdõ lépések megtételében segít. Mindenesetre leckék szorgos végigcsinálásával megismerhetjük, hogyan kell egyszerû algoritmusokat Jáva nyelvre lekódolni és ez egy olyan alap, amin bí zvást építkezhetünk tovább, ha van kedvünk vagy idõnk a késõbbiekben. A tananyagban két sajátos módszert használunk fel. Elõször is "munkafüzet" stí lusban építkezünk, tehát az új ismereteket gyakorlatokkal rögzítjük. A gyakorlatok elvégzése az anyag integráns része, elvégzésük nélkül az ismeretek megfelelõ rögzítõdése nem várható. A második fogás a "varázselemek" módszere: tekintve, hogy az olvasó nem rendelkezik az alaptudással, viszont azonnal csinálunk valami mûködõt, nem magyarázhatunk meg rögtön mindent. Az ilyen részeket "varázselemnek" nevezzük, a példaprogramokban kékkel jelöljük és az olvasótól azt várjuk, fogadja el, hogy ezek "kellenek". Remélhetõen az anyag végére semmi nem marad "kék". Ugyancsak tudatos választás az objektum fogalmának és használatuknak a késõi bevezetése. Felfogásom szerint az objektumok nagy méretû programok és újrahasználható komponensek írásában alapvetõek, kezdõ olvasónk problémái azonban másfélék lesznek az elején. Elkövetem azt az eretnekséget tehát, hogy a tananyag elsõ részében az objektumokról szót se ejtek és a Jávát mint sima funkcionális nyelvet használom, még ha ezzel ki is hí vom az objektumorientált vallás hí veinek átkait. Még egy megjegyzés: ez a tananyag azért hozzáférhetõ a Weben, hogy folyamatosan nõjön, változzon. Í rása közben jöttem rá, milyen régen voltam magam is kezdõ és mennyire nem láthatók már nekem, hol vannak a nehezen érthetõ részek. Ha tehát ilyet találsz, írj azonnal egy levelet nekem és én megpróbálom a kérdéses részt világosabbá tenni. Külön köszönettel tartozunk kolléganõinknek, akik eljátszották a kísérleti nyúl szerepét és elõször szenvedték át magukat az anyagon, annak ellenére, hogy semmi közük nem volt korábban a

Transcript of Bevezetes a Java Programozasba

Page 1: Bevezetes a Java Programozasba

Bevezetés a programozásba a Jáva nyelven keresztül

Szerzõk: Paller Gábor, Páskuj Attila

Az olvasó most egy merész kísérlettel fog találkozni. Ebben a cikksorozatban megpróbálkozunk azzal, hogy egy általános reál tudással rendelkezõ olvasót beavassunk a számítógépprogramozásba és erre eszközül a Jáva nyelvet használjuk fel. A szokásos módszer az lenne, hogy a tanuló elõbb ismerje meg az algoritmikus alapokat és legalább egy funkcionális nyelvet, majd ezután vágjon bele egy olyan relatí ve bonyolult rendszer megismerésébe, mint a Jáva. Ezzel a módszerrel én egyetértek, ugyanakkor az elmúlt évben sok olyan panaszt hallottam, miszerint a "belépési szint" a Jávába túl magas, a tankönyvek megértéséhez az objektumorientált programozási paradigmát már ismerni kell. Nem mindenkinek van kedve, ideje vagy lehetõsége azonban végigjárnia az alapos tanuló útját és a tapasztalat azt mutatta, hogy ilyen esetekben is jó eredmények érhetõk el megfelelõen adaptált és fókuszált tananyaggal. A kezdõ tudás ugyan nem lesz olyan mély és a tanuló algoritmikus tudása is gyengébb lesz hagyományos képzést végzetteknél, azonban gyakorlattal sok minden pótolható és igen sok programozási feladatnál nincs szükség komplex algoritmusok kifejlesztésére vagy kódolására. Természetesen a fenti, redukált cél egyben intés is a diáknak: ne gondolja, hogy azért mert végigcsinálta ezt a leckesorozatot, azonnal képzett programozó lett belõle. Az algoritmikus alapok bõvítésére a késõbbiekben nagy szükség lesz és ez az anyag csak a kezdõ lépések megtételében segít. Mindenesetre leckék szorgos végigcsinálásával megismerhetjük, hogyan kell egyszerû algoritmusokat Jáva nyelvre lekódolni és ez egy olyan alap, amin bí zvást építkezhetünk tovább, ha van kedvünk vagy idõnk a késõbbiekben.

A tananyagban két sajátos módszert használunk fel. Elõször is "munkafüzet" stí lusban építkezünk, tehát az új ismereteket gyakorlatokkal rögzítjük. A gyakorlatok elvégzése az anyag integráns része, elvégzésük nélkül az ismeretek megfelelõ rögzítõdése nem várható. A második fogás a "varázselemek" módszere: tekintve, hogy az olvasó nem rendelkezik az alaptudással, viszont azonnal csinálunk valami mûködõt, nem magyarázhatunk meg rögtön mindent. Az ilyen részeket "varázselemnek" nevezzük, a példaprogramokban kékkel jelöljük és az olvasótól azt várjuk, fogadja el, hogy ezek "kellenek". Remélhetõen az anyag végére semmi nem marad "kék".

Ugyancsak tudatos választás az objektum fogalmának és használatuknak a késõi bevezetése. Felfogásom szerint az objektumok nagy méretû programok és újrahasználható komponensek í rásában alapvetõek, kezdõ olvasónk problémái azonban másfélék lesznek az elején. Elkövetem azt az eretnekséget tehát, hogy a tananyag elsõ részében az objektumokról szót se ejtek és a Jávát mint sima funkcionális nyelvet használom, még ha ezzel ki is hí vom az objektumorientált vallás hí veinek átkait.

Még egy megjegyzés: ez a tananyag azért hozzáférhetõ a Weben, hogy folyamatosan nõjön, változzon. Í rása közben jöttem rá, milyen régen voltam magam is kezdõ és mennyire nem láthatók már nekem, hol vannak a nehezen érthetõ részek. Ha tehát ilyet találsz, írj azonnal egy levelet nekem és én megpróbálom a kérdéses részt világosabbá tenni.

Külön köszönettel tartozunk kolléganõinknek, akik eljátszották a kísérleti nyúl szerepét és elõször szenvedték át magukat az anyagon, annak ellenére, hogy semmi közük nem volt korábban a

Page 2: Bevezetes a Java Programozasba

számítógépes programozáshoz. Véleményük, visszajelzéseik nagy segítségünkre voltak az anyag taníthatóságának javí tásában.

Paller Gábor, Páskuj Attila

Tartalomjegyzék 1. fejezet

Elõttünk egy számítógép: hogy jön mindez a Jávához? Processzor, memória és a maradék: mindezeket programok vezérlik. Programszöveg nekünk és program a számítógépnek: a fordítóprogramok. Egy érdekes megoldás a Jávában: a virtuális gép. Elsõ Jáva programunk.

2. fejezet

Fiókos szekrény szilíciumból. A számítógép mindent képes tárolni, csak mi nem felejtsük el, mit hová tettünk: a változók. Fiókméretek és adattípusok. Két egyszerû adattípus, az egész és a lebegõpontos. Néhány alapmûvelet kezdetnek.

3. fejezet

Terelgetjük a számítógépet; a program futásának menete. Struktúrált programozás, építkezés Matrjoska babákból. Elágazások és logikai kifejezések. Megdolgoztatjuk a gépet: a ciklusok.

4. fejezet

Megjegyzések. Írni utálunk, ezért törekszünk az újra felhasználható programrészekre. Függvények a matematikában és Jávában. Paraméterek, visszatérési értékek és változó láthatósági szabályok.

5. fejezet

Matematikai függvények, a Jáva matematikai függvényei és azok használata.Egyszerû matematikai problémák programnyelvi megoldásai. Feladatok a függvények használatára.

6. fejezet

További gyakorló feladatok matematikai problémák megoldására. Cimke. Többszörös elágazás. Kilépés programblokkból: break. Kilépés ciklusból: continue. 2 ismeretlenes lineáris egyenletrendszerek megoldási módszerei és azok JAVA nyelvû megvalósítása. A mátrixelmélet elemei: mátrix, determináns. A 3- és többismeretlenes egyenletrendszerek megoldási lehetõségei és JAVA nyelvû megvalósítása.

7. fejezet

Fiókos szekrények garmadája, mindegyik hozzá való mamával. A Jáva alapépítõelemei, az objektumok. Objektumok deklarálása változókkal és függvényekkel, amelyeket ezek után metódusoknak fogunk hívni. Objektumok létrehozása és halála, életciklus a Jávában. A szemétgyûjtõ.

8. fejezet

Újabb megvilágosodás: statikus változók és metódusok. Egyedváltozók és osztályváltozók kavalkádja. Öröklõdés és konverziók.

9. fejezet

Adatok tömegesen: tömbök a Jávában. Tömbtípusok. A Jáva tömbök is csak objektumok. Objektumok és tömbök tömbje.

10. fejezet

Nem csak számok vannak a világon! Dolgozzunk érdekesebb adatokkal: karakterek és azok halmazai. Karaktertípus a Jávában, a char típus. Karaktersorozatok avagy ismerkedés a String osztállyal. String és StringBuffer, a két jóbarát.

11. fejezet

A Jáva osztályok is csak fájlok; Jáva osztályok elhelyezése és fellelése. További káoszteremtõ eszközök: package és import. Jó helyek a fájlrendszeren: a CLASSPATH környezeti változó.

Page 3: Bevezetes a Java Programozasba

12. fejezet

Mindenki a saját hibáinak kovácsa: személyre szabott hibajelzések a Jávában. Kivételek élete és halála: throw utasítás, a throws kulcsszó valamint a try-catch blokk.

Folytatása következik (C) 2001, Paller Gábor, Páskuj Attila. Ez a tananyag részekben vagy egészben, módosítással vagy anélkül korlátozás nélkül felhasználható non-profit célokra. Jövedelemszerzés céljából történõ felhasználásához a szerzõk elõzetes írásbeli engedélye szükséges.

1. fejezet Elõttünk egy számítógép: hogy jön mindez a Jávához? Processzor, memória és a maradék: mindezeket programok vezérlik. Programszöveg nekünk és program a számítógépnek: a fordítóprogramok. Egy érdekes megoldás a Jávában: a virtuális gép. Elsõ Jáva programunk.

Ahhoz, hogy elolvashasd ezt a szöveget, számítógépet kell használnod és a lecke munkafüzet jellege miatt is szükséged lesz rá. Bizonyára a gép használata már meglehetõsen ismert, különben nem vágtál volna bele egy programozási tanfolyamba. Mindazonáltal tisztázzuk, alapvetõen milyen vadállat megszelídítésére vállalkoztunk. A számítógép feladata adatok feldolgozása. Bármit meg tud velük csinálni, összeadja, kivonja, mozgatja a memóriájában és a hozzá csatolt eszközök között. Minthogy ezt jóval gyorsabban csinálja, mint egy ember, nem lehet úgy irányítani, mint egy markológépet; mire megkapja a következõ emberi parancsot, azalatt elrepül sok millió mûvelet végrehajtási lehetõsége. Ezen okból a számítógépnek az elvégzendõ mûveletsorozatot elõre mondják meg, ez a program. A számítógép memóriája számok tárolására szolgál, í gy a programot magát is számsorozat formájában tárolják. Neumann János nagy felismerése volt, hogy a program és az adat alapvetõen ugyanolyan típusú információ és ezért ugyanabban a memóriában tárolható. Az alábbi képen egy Neumann-típusú számítógép blokkvázlata látható.

1. ábra: Neumann-számítógép blokkvázlata

A legfontosabb komponens a CPU, a Central Processing Unit, röviden a processzor. Ez a számítógép központi vezérlõegysége, ez hajtja végre a memóriában elhelyezett programot. Az egy áramköri lapkán, chip-en (ezek azok a kis fekete lapos dobozok a számítógép áramköri lapján) megvalósított processzort mikroprocesszornak nevezzük. Minthogy manapság szinte mindegyik elterjedt processzor ilyen, a két elnevezést ma már felcserélhetõen használják.

A memóriában helyezkednek el azok az adatok, amikkel a processzor mûveleteket tud végezni illetve a programja, amit végrehajt. A memória, mint már írtam, számsorozatok formájában tárolja a programot. Egy szám, mint programutasí tás jelentését a processzor tervezésekor döntik el. A

Page 4: Bevezetes a Java Programozasba

processzor által támogatott összes utasí tás számokhoz rendelése a gépi kód. A pusztán számokkal leírt, a processzor számára közvetlenül értelmezhetõ programot gépi kódúnak nevezzük. A gépi kód jelentése processzortípusonként változik, ez az egyik oka annak, hogy egyik típusú gépre írt program nem megy a másik típusú gépen.

Nagyon sokféle memória létezik, de ebben a pillanatban két csoportba osztjuk õket: felejtõ és nem felejtõ memóriákra. A felejtõ memória tartalma kitörlõdik, amikor a gépet kikapcsoljuk, ezt RAM-nak (Random Access Memory) is nevezik. A felejtésért cserébe a RAM írható és olvasható is, mégpedig mindkét mûveletet elég gyorsan meg lehet vele csinálni. A másik fajta memória nem felejti el a tartalmát, amikor a gépet kikapcsolják, ennek egyik alaptípusát ROM-nak (Read Only Memory) nevezik. Amint a neve is mondja, a ROM-ot csak olvasni lehet, a tartalmát a gyártás során állítják be. Ezen felül vannak még köztes típusok, pl. a Flash memóriák, amik nem felejtõk és írhatók is, viszont relatí ve lassú õket írni.

A számítógépnek nem sok haszna van, ha csak a memóriájában levõ adatokat rendezgeti teljes elszigeteltségben, valahogy bele kell juttatni adatokat majd kinyerni belõle. Erre szolgálnak a perifériák vagyis a be-kiviteli egységek. Periféria pl. a billentyûzet, vagy a képernyõ, amin ezt olvasod, a merevlemez, az egér, a hálózati csatoló, stb. Nagyon bonyolult perifériák vannak, legtöbbjük (pl. a merevlemezed vagy a billentyûzeted) maga is egy kis specializált számítógép.

A számítógép egységeit egy adatút köti össze, ezt rendszerbusznak nevezzük. Bármely két (vagy több) egység képes itt kommunikálni oly módon, hogy az adatszolgáltató egység ráteszi az adatát a buszra, a fogadó(k) pedig leolvassa (leolvassák). Általában a CPU irányítja ezt a tevékenységet, de ez nem törvényszerû, intelligens perifériák maguk is képesek adatokat a memóriába írni. Modern számítógépekben nem egy busz van, hanem több is, de a programozó számára általában csak az öreg Neumann-architektúra látszik.

Amikor a gépet bekapcsolod, a processzor azonnal vad tevékenységbe fog és ezt nem is hagyja abba kikapcsolásig. A vad tevékenység azt jelenti, hogy egy megadott helyen elkezdi végrehajtani a memóriában tárolt programot. Jó, ha ott valami értelmes program van (ne feledd: minden memóriatartalom végrehajtható, hiszen minden program csak egy számsor), erre jó a ROM. Ide általában valami olyan programot tesznek, ami végeredményben lehetõvé teszi a felhasználónak, hogy kommunikáljon a géppel, begépelje a parancsait és elolvashassa a választ. Azért írom, hogy "végeredményben", mert a modern nagy számítógépekben ez egy elég bonyolult folyamat végeredménye, mivel az alapprogram is igen nagy. A ROM-ban elhelyezett programocska nem csinál mást, mint valami perifériáról feltölti azt a megalomániás valamit, ami manapság egy modern operációs rendszer (Operating System, OS). Az operációs rendszer egy programcsomaggyûjtemény, ami tartalmazza mindazt, ami egy program és a felhasználó számára szükséges a gép használatához. Egy program olyan alapfunkciókat igényelhet az operációs rendszertõl, mint egy fájl megnyitása, törlése, í rása, olvasása, í rás a képernyõre, olvasás a billentyûzetrõl, kommunikáció más gépekkel a hálózaton keresztül, stb. A felhasználó számára lehetõvé teszi programok indí tását, leállí tását, bejelentkezést, kijelentkezést, stb. Vannak funkciók, amelyeknél igen nehezen eldönthetõ, az operációs rendszerhez tartoznak-e, pl. a grafikus felület kezelése. Egy biztos: a dolog, amivel a géped több percig szórakozik bekapcsolása után, az operációs rendszer elindí tása.

Az összes többi, kevésbé általános program az operációs rendszer irányítása alatt és annak szolgáltatásait felhasználva fut, ezeket alkalmazásoknak hí vjuk. Mi is ilyen alkalmazásokat fogunk írni. Az alkalmazások gépi kódban töltõdnek be és értelmezõdnek. Roppant kellemetlen lenne

Page 5: Bevezetes a Java Programozasba

azonban direkt gépi kódban programot írni és még kellemetlenebb karbantartani, tehát javítani, bõvíteni. Gondold meg: fejben kell tartani minden utasí táshoz tartozó kódot és megérteni pedig a számokból kell a programot. Ugyancsak nem elõnyös, hogy a gépi kódú utasí tások nagyon alacsony szintûek, egy betû képernyõre írása több száz sor is lehet. Dacára tehát annak, hogy a programunk mindig (még a Jávában is) a processzor saját nyelvén, tehát gépi kódban fut le, szinte sohase ebben írják, hanem valamilyen, ember számára jobban emészthetõ formában.

A legegyszerûbb ilyen forma az a kódolás, amikor a gépi kód minden egyes utasí tásának egy szöveges utasí tást feleltetnek meg, ez a nyelv az assembly. Az assembly-ben könnyebbség, hogy nem kell számokra emlékezni, de továbbra is adott az utasí tások egyszerûsége, assembly-ben nagyon munkaigényes programot írni. Az assembly programot a processzor természetesen nem képes végrehajtani (hiszen az csak ember számára olvasható (olvasható?) szöveg), elõbb kell egy program, az assembler, ami gépi kódú programot csinál belõle. Amióta a PC-k olyan nagyok lettek, mint régen egy miniszámítógép, az assembly használata visszaszorult, de mindenhol használják, ahol az erõforrások (processzor, memória) szûkösek vagy a feladat olyan gépet próbáló, hogy minden teljesítményt ki kell belõle facsarni.

A fenti trükköt, miszerint a programot valamilyen ember által könnyebben érthetõ formában írjuk, amit aztán egy másik program segítségével etetünk meg a számítógéppel, széles körben használják és nem csak olyan egyszerû nyelvekkel, mint az assembly. Ha az általunk írt förmedvényt (avagy zseniális programot) amúgy is el kell olvasnia egy másik programnak, hogy a géppel megetesse, akkor nem kell a gépi utasí tások alacsony szintjéhez kötõdnünk, kényelmesebb nyelveket is létrehozhatunk í zlésünk szerint. Í gy jöttek létre a számítógépes nyelvek tucatjai, a LISP, a FORTRAN, a C, a BASIC, a Pascal, a C++ és a Jáva, hogy a többiekrõl ne is beszéljünk. Ezek a nyelvek elsõ pillantásra nagyon különböznek, de igazából csak néhány alapkoncepció variálására épülnek. Ahogy az egyik kalapács lehet jó és a másik rossz, úgy vannak jó és rossz nyelvek is. Sõt, ahogy az egyik kalapács jó lehet kõtörésre, a másik cipészkedésre, feladathoz is érdemes nyelvet választani. Az eddig legsikeresebbnek bizonyult programozási nyelv a C ill. az objektumorientált bõvítése a C++, ezt a két nyelvet a rendszerprogramozástól csillagászati programok készítéséig mindenre fel lehet használni. A Jáva nem ennyire sokoldalú, azonban felhasználói alkalmazások, különösen hálózati alkalmazások jóval gyorsabban és kevesebb hibalehetõséggel rakhatók benne össze, ezért ebben az alkalmazási szegmensben gyorsan terjed.

Mint megállapítottuk, minden programnál, ami nem gépi kódban íródott, kell egy másik program, ami a gép szájába rágja, mit is akartunk mondani. Pontosan, mintha a munkásunk csak eszperantóul beszélne, mi pedig netán csak magyarul tudnánk kommunikálni, kell valaki, aki lefordítja neki. Mint az emberi fordí tásoknál, itt is sokféle felállás lehetséges. Például leírhatjuk az instrukcióinkat, azt egy tolmács lefordíthatja eszperantóra és odaadhatja a munkásnak, aki ezek után elvégzi a feladatot. Világos, hogy a munkásnak ez jó, hiszen amikor dolgozik, már nem kell a fordí tással törõdnie, ugyanakkor az instrukciók korrekt lefordí tása munkaigényes feladat és meg se változtathatjuk õket menet közben. A fenti példához teljesen hasonló módon és ugyanazokkal az elõnyökkel-hátrányokkal mûködnek a compiler-ek vagy magyarul a fordítóprogramok. Ezek a programok megeszik az általunk írt programot és közvetlenül végrehajtható gépi kódot generálnak belõle. A legfõbb elõny a gépi kódra fordított program gyors futási sebessége és a jó minõségû fordítás, hiszen a fordítóprogramnak egy csomó ideje van gondolkodni a legjobb variáción. Példának okáért a C, C++ és a FORTRAN nyelvek leggyakoribb implementációi fordítóprogramot alkalmaznak.

Page 6: Bevezetes a Java Programozasba

Másik lehetõség, hogy szinkrontolmácsot alkalmazunk és miközben mi elõadjuk a mondókánkat, õ menetközben fordítja le eszperantóra. Hátrány persze, hogy a fordí tás ideje hozzáadódik a munkavégzés idejéhez, hiszen amí g a munkás a tolmácsot hallgatja, nem dolgozik. Bizonnyal a fordítás se a legpontosabb. Ugyanakkor kimarad az elõzetes fárasztó külön fordítási menet és menetközben is megváltoztathatjuk az instrukciókat. A fenti séma az interpreter-ek vagyis az értelmezõprogramok megoldása. Itt a programunkat az értelmezõprogram futásidõben fordítja le. Elõnye a rendkí vül gyors fejlesztési idõ (nincsenek lassú fordí tási menetek) és elvileg lehet olyan programot is írni, ami újraírja önmagát. Hátrány a végrehajtási sebesség. A LISP és a BASIC legtöbbször értelmezõprogramos megvalósí tásban szokott rendelkezésre állni.

Tegyük fel, hogy eszperantó anyanyelvû alkalmazottunk mellett van olyan is, aki csak belgául tud. Akár a fordítóprogramos, akár az értelmezõprogramos megoldást választjuk, mindenképpen meg kell duplázni a fordítók számát. Pontosan ugyanez a probléma akkor is, ha szempont az, hogy minnél többfajta gépen menjen a programunk. Mindegyikben más processzor lenne, ami más gépi kódot ért, más fordító fog kelleni. Áthidaló megoldásként kitalálhatjuk, hogy soknyelvû alkalmazottainkkal piktogrammokkal fogunk kommunikálni. Tolmácsunk az instrukciókat piktogrammnyelvre fordítja, amit kitûnõ munkásaink sokkal gyorsabban fognak fel, mintha magyarul mondanánk nekik, habár nem olyan gyorsan, mintha az anyanyelvükön volna leírva. Ezzel a némileg idióta hasonlattal el is jutottunk a közbülsõ kódot alkalmazó nyelvekhez, mint amilyen a Jáva.

A Jáva nyelv nagy í gérete, és korlátai mellett az elterjedésének legnagyobb segítõje, hogy a nyelv egy saját kis világot hoz létre a hordozó processzoron és operációs rendszerben, amelyben minden Jáva program mindenhol lefuthat. Olyan ez, mint amikor világutazásunk közben mindegy, hol térünk be egy McDonald's-ba, Kínában vagy Nicaraguában, mindenhol ugyanazt kapjuk. A Jáva programok a Jáva virtuális processzor gépi kódjára fordítódnak és a Jáva virtuális operációs rendszer szolgáltatásait veszik igénybe. Ezt persze egy konkrét gépen ill. operációs rendszeren a Jáva futtatókörnyezet, a Jáva Virtuális Gép (Java Virtual Machine, JVM) kell fogadja és megoldania a Jáva gépi nyelv (a Jáva bájtkód) futtatását valamint a Jáva operációs rendszer szolgáltatásainak (a Jáva osztálykönyvtárnak) igénybevételét. Ez idõbe kerül, í gy Jáva programok sohase lehetnek olyan gyorsak, mint pl. a C-ben írtak. A sebesség azonban másodlagos lehet olyan elõnyök mellett, hogy pl. a Jáva programot gond nélkül lehet PC-n fejleszteni, majd változtatás nélkül telepíteni valami esztelen méretû számítógépre. Maga a Jáva futtatórendszer az operációs rendszer számára egy egyszerû alkalmazás, habár történtek már kísérletek az operációs rendszerrel való integrálására. A miáltalunk használt változat egyszerû alkalmazásként fut.

Az alábbi ábrán ez a koncepció látható. Meg kell jegyezni, hogy a JVM és a Jáva osztálykönyvtár implementációja természetesen szintén függ az operációs rendszertõl és a processzortól, de a rajta futó Jáva bájtkód már ugyanaz.

Page 7: Bevezetes a Java Programozasba

2. ábra. A Jáva program, a JVM, az operációs rendszer és a processzor kapcsolata

Ennyi csigázás után talán csapjunk is a lovak közé. Két dologra lesz szükségünk: egy installált Jáva futtatórendszerre (Java SDK) és egy szövegszerkesztõre. A szövegszerkesztõnek tudnia kell egyszerû szöveges fájlokat kezelnie, ilyen pl. a népszerû Notepad Windows rendszerekben. Számos oknál fogva a Word vagy hozzá hasonló komoly szövegszerkesztõ nem praktikus, habár használható. Érdemes szerezni egy programozóknak szánt szövegszerkesztõt (én Windows-on a GWD szövegszerkesztõt használom), az ilyenek el vannak látva minden csingilingivel, például szí nezik a forrásszöveget, ami javítja az áttekinthetõségét. Ha azonban nem akarsz ilyen kalandokba belemenni, jó az öreg Notepad.

Kell még a Java SDK. Lehet. hogy a gépeden már meg is van, tesztelésképpen add ki a java ill. a javac parancsokat a parancsértelmezõ ablakában (Command prompt). Ha ezek végrehajtódnak (képernyõnyi segítõ szöveget írva ki), más már nem is kell. Ha nem, fordulj

Kitérõ: a Java SDK installálása

fejezetünkhöz. Most pedig lássuk elsõ programunkat:

public class Elso { public void prog( String args[] ) { System.out.println( "Hello, ez az elso Java programunk!" ); } public static void main( String args[] ) { Elso e = new Elso(); e.prog( args ); } }

Mentsd el a fenti szöveget az Elso.java nevû fájlba. Fontos, hogy a fájl neve mindenképpen ez legyen! Jóval késõbb fogjuk kifejteni, miért , de a forrásban a "public class" mögött levõ névvel megegyezõnek kell legyen a forrásfájl neve .java kiterjesztéssel. Most mondd azt:

javac Elso.java

Ha minden jól ment, a parancs némi motozás után üzenet kií rása nélkül befejezi a futását. Ha most megnézed a könyvtárat, ahol a forrásszöveg volt, találsz egy Elso.class nevû fájlt. Ez a lefordított Jáva program, ebben van benne a programod Jáva bájtkódban. Most már csak egy Jáva virtuális gépet kell találnunk, ami le is futtatja. A java parancs pont ezt tudja.

Page 8: Bevezetes a Java Programozasba

java Elso

És megjelenik a következõ szöveg:

Hello, ez az elso Java programunk!

Hihetetlen, már mûködõképes programot írtunk! Igaz ugyan, hogy az egészbõl nem értünk semmit, de elsõ lépésnek nem rossz! A sok kékséggel most ne foglalkozzunk, egy sort azért elmagyarázok. A System.out.println arra jó, hogy amit a mögötte levõ zárójelek közé írtunk, kiírja a konzolra. Az idézõjelek fontosak: ez mondja meg a fordítóprogramnak, hogy amit mögötte lát, az nem programszöveg, nincs értelme a számítógép számára, csak egy, a gép számára értelmetlen, önkényes karaktersorozat, vagyis betûk, számok és szimbólumok sora. Errõl bõvebben majd egy késõbbi fejezetben! Végül az utasí tást egy pontosvesszõ zárja, ez kötelezõ. Minden Jáva utasí tás végén pontosvesszõ kell legyen.

Azt tanultuk a fejezet elején, hogy a számítógépnek a program elõre mondja meg, mit kell majd csinálnia. Ugyan a mi kis programunk elindí tásakor már í gy is jó sok mindent csinál (el se tudod képzelni, mennyi mindent!), mégis, demonstráljuk ezt a képességét most szuperprogramunk egy jelentõs bõvítésével: írassunk ki vele egy újabb sort.

public class Masodik { public void prog( String args[] ) { System.out.println( "Hello, ez az elso Java programunk!" ); System.out.println( "Ez pedig egy masodik sor tole" ); } public static void main( String args[] ) { Masodik e= new Masodik(); e.prog( args ); } }

Másoljuk ki a szövegbõl a szövegszerkesztõbe, mentsük el, fordítsuk le és futtassuk. (Most utoljára súgok: Copy-Paste Notepad-be, elmenteni a Notepad-bõl Masodik.java néven, javac Masodik.java, java Masodik). A futtatási eredmény nem meglepõ:

Hello, ez az elso Java programunk! Ez pedig egy masodik sor tole

Figyeljük meg, hogy a két szöveg egymás alá íródott ki. Ez a System.out.println tulajdonsága, ez minden, általa kiírt karaktersorozat után egy soremelést is kiír. A soremelés egy olyan speciális karakter, ami a következõ karakter kiírása pozícióját egy új sor elejére lépteti.

Feladat:

írasd ki a saját nevedet a számítógéppel!

Ezzel a lecke végére is értünk. Jókora utat jártunk be, a számítógép fogalmától eljutottunk egy mûködõképes Jáva programig. A következõ fejezetben bemutatjuk, miféle számításokat tudunk végeztetni szilíciumagyú barátunkkal.

Ellenõrzõ kérdések

Page 9: Bevezetes a Java Programozasba

I. Mi a számítógépes program?

II. Mi a processzor szerepe a számítógépben?

III. Mi az operációs rendszer?

IV. Miért szükségesek fordítóprogramok?

V. Miért elõnyös a McDonald's?

VI. Mi a különbség a gépi kód és a Jáva bájtkód között?

VII. Mi a Jáva virtuális gép?

VIII. Milyen parancs indítja el a Jáva fordítóprogramot?

IX. Milyen parancs indítja el a Jáva virtuális gépet?

X. Mi az a Jáva szerkezet, amivel a konzolra lehet írni?

XI. Mit jelent a karakter fogalma?

XII. Mi az a soremelés karakter?

2. fejezet Fiókos szekrény szilíciumból. A számítógép mindent képes tárolni, csak mi nem felejtsük el, mit hová tettünk: a változók. Fiókméretek és adattípusok. Két egyszerû adattípus, az egész és a lebegõpontos. Néhány alapmûvelet kezdetnek.

A számítógép olyan, mint a mamátok fiókos szekrénye. Ez egy nagyon jó hasonlat, magamtól nem is tudtam volna kitalálni, Roger Kaufman örökbecsû FORTRAN képeskönyvébõl vettem. FORTRAN-t ma már kevesen használnak, de a könyv még ma is a legjobb bevezetõ kezdõknek a számítógépes programozásba. De vissza a fiókos szekrényhez! A szekrénynek, akarom mondani a gépnek rengeteg fiókja van a legkülönbözõbb cuccok tárolására. A gép persze nem zoknikat vagy több éves villanyszámlákat tárol, hanem adatokat. Hogy a sok fiók között ne vesszünk el, megcí mkézzük a fiókokat, egyszerû és találó neveket adunk nekik. Egy ilyen fióknév egy változó neve, tartalma pedig a változóban tárolt érték.

A változó egy igen fontos fogalom, ezért lássunk néhány hasonlatot. Pénztárcánkban levõ pénz változó mennyiség, ez megfelel a változó értékének. Egy konkrét pénztárca megfelel a változónak magának, holott pénztárcák általában nincsenek felcí mkézve. Vagy vegyünk egy teherautót: a rajta levõ sóder mennyisége változó érték, a teherautó rendszáma pedig azonosítja, melyik értéket keressük.

A változó egy analógia, ami lehetõvé teszi nekünk, hogy a gép sok milliónyi memóriarekeszébõl kiválasszuk a minket érdeklõt. Egy változó mindig egy adag memóriarekeszre vonatkozik (hogy mennyire, azt majd az adattípusok tárgyalásánál meglátjuk) és a Jáva nyelv azt a kényelmességet adja

Page 10: Bevezetes a Java Programozasba

nekünk, hogy erre az adag memóriarekeszre egy általunk választott névvel hivatkozhatunk. A névnek egyedinek kell lennie és betûvel kezdõdnie, a további karakterei lehetnek betûk, számok vagy aláhúzásjel. Néhány példa érvényes változónevekre:

• i • j17 • PI • nagyon_hosszu_valtozonev

Itt van három érvénytelen is:

• 32ev • a$ • nagyon hosszu valtozonev

A Jáva nyelv különbözõnek veszi a kis- és nagybetûket, így a pi és a PI két külön változót jelöl.

Mint ahogy a fiók kiválasztásánál is fontos, hogy cetliket tárolunk-e benne vagy lábosokat, úgy a számítógépnek is tudnia kell, mennyi memóriát kell egy változóhoz rendelnie. Ezt az határozza meg, milyen típusú adatokat tárolunk a fiókban, akarom mondani a memóriarekeszben. A tárolni kí vánt adat típusát a változó adattípusának megadásával kell meghatároznunk, hasonló módon, mint ahogy a változó nevét is a változónévvel. A Jáva nyelvnek van néhány beépített adattípusa továbbá a képessége, hogy új adattípusokat hozhasson létre a programozó. Most csak két adattípus vizsgálunk meg, az egész és a lebegõpontos típust.

Az egész típus a számítógépünk legalapvetõbb típusa, leginkább ezzel szeret számolni a gép, ezért mi is használjuk, ahol csak lehet. Az egész típusú szám 0-tól nagyjából 2 milliárdig nõhet és csökkenhet pozitív és negatív irányban egyaránt és nevéhez illõen törtrésze nem lehet. A típust az int kulcsszó leí rásával deklaráljuk. Pl.:

int i; int K,nagyon_hosszu_valtozonev;

A deklaráció a Jáva nyelvben kötelezõ. Minden változót elõbb deklarálni kell és csak aztán lehet használni. Deklarálás bárhol lehet a programban a változó felhasználása elõtt, tehát akár a program közepén is. A deklarálás hatására a változóhoz hozzárendelõdik a tárterület, ahol a változó értékét tárolja gépünk, de a változó értékérõl semmit sem tudunk. Sõt mi több, a Jáva fordító, ha felderíti, hogy olyan változót használtunk, aminek az értékérõl még nem rendelkeztünk, hibaüzenetet küld.

De mit beszélek én itt a változó értékének megadásáról és a változó felhasználásáról, amikor még csak a deklarálást ismerjük? Ha egy változót már deklaráltunk, következõ lépésben adjunk neki gyorsan értéket. Pl.:

i = 3; K = -10;

Ez az értékadó utasítás. Fontos, hogy ne tekintsd az értékadó utasítást a matematikából ismert egyenlõség teljes szinoní májának. A matematikai egyenlõség azt fejezi ki, hogy két kifejezés a levezetés teljes idõtartamára bizonyos feltételek mellett megegyezik. Ha tehát y = 2x, akkor ez í gy van bármilyen x-re a megadott peremfeltételek mellett. Az értékadó utasí tás azonban, hogy a FORTRAN képeskönyvbõl orozzak megint, egy "bemén" jel, tehát abban a pillanatban, amikor

Page 11: Bevezetes a Java Programozasba

kiértékelõdik, a jobb oldalán egy szám lesz, ami beleíródik, "bemén" a változó mögött meghúzódó memóriába csak egyszer és akkor, amikor az utasí tás végrehajtódik. Ha késõbb frissíteni akarjuk a változó értékét, újra végre kell hajtanunk egy értékadó utasí tást. Ha tehát én azt mondom, hogy y = 2*x, akkor a gép veszi az x változó értékét, megszorozza kettõvel és beteszi az y változóba (figyelj, mostantól slamposabban fogalmazok! eddig mindig hangsúlyoztam, hogy a változónév mögött hozzárendelt memória húzódik meg, de remélhetõleg ez már jól a fejedbe vésõdött. Mostantól csak azt mondom, hogy, beleíródik a változóba). Ez csak egyszer és az x aktuális értékével történik meg, ha x késõbb megváltozik és fontos, hogy ezt y is kövesse, újra végre kell hajtani az értékadást.

Suttyomban megint elõreszaladtam egy kicsit és leírtam egy Jáva kifejezést. Végre is erre tartjuk a számítógépet, számoljon gyorsan. A Jáva program is mindent képes kiszámolni, ami leírható egy véges mûveletsorozattal, azaz algoritmussal. A Jáva program jónéhány mûveletet képes számokkal elvégezni, ezek közül nézzük most a négy alapmûveletet.

• + : összeadás • - : kivonás • * : szorzás • / : osztás

A mûveletek a matematikában megszokott módon precedenciával rendelkeznek, tehát zárójelek nélkül a szorzás és az osztás "erõsebb" az összeadásnál és a kivonásnál. Ezen (szokásos módon) zárójelekkel változtathatunk. Pl. a 5+2*10 értéke 25, míg az (5+2)*10 értéke 70. Zárójeleket tetszõleges mélységben ágyazhatunk egymásba, csak párosan legyenek. A kifejezésekben a konstansokat, esetünkben egész számokat és a változókat egyenértékûen használhatjuk. Ha tehát az y változóba az x változó kétszeresét akarnád tenni, a helyes válasz: y = x*2. Lássuk ezt egy mûködõ programban is!

public class Szamit1{ public void prog( String args[] ) { int x,y; x = 5; y = x*2; System.out.println( "Eredmeny: "+y ); } public static void main( String args[] ) { Szamit1 e = new Szamit1(); e.prog( args ); } }

Nem hiszem, hogy a System.out.println során kí vül bármi is magyarázatot igényelne. A már megismert karaktersorozat-konstanshoz (az idézõjelek között levõ részhez) hozzáfûztük az y változót, ami egy egész. Ekkor a Jáva fordító automatikusan olyan kódot fordít, ami az y-ban levõ értéket karaktersorozattá alakítja és hozzáfûzi a karaktersorozat-konstanshoz. Tehát az eredmény:

Eredmeny: 10

Feladat

írj egy olyan programot, ami kiírja 2 elsõ nyolc hatványát. A kií rási kép legyen tehát: 2^0 = 1 2^1 = 2

Page 12: Bevezetes a Java Programozasba

... 2^7 = ....

Oldd meg a feladatot önállóan, majd a saját programodat hasonlítsd össze a megoldással .

Ugye nem ért váratlanul az elõzõ feladatban az n=n+1 típusú értékadás? Matematikailag ez egy teljes csõd, de számítógépül annál több értelme van: vedd az n értékét, adj hozzá egyet, majd tárold vissza n-be, vagyis növeld meg n-t eggyel.

Most csavarjunk egyet a dolgon es 0-tol kezdve negatí v irányba haladjunk kettõ hatványaival. Vagyis a kií rási kép legyen: 2^0 = 1 2^-1 = 0.5 ...

(Ugye emlékszel: valaminek a negatí v hatványa megegyezik a pozití v hatvány reciprokával. Vagyis: 2-

4 = 1/24 ).

Ha programunkat lelkesen megmódosítanánk úgy, hogy az összes n = n + 1-et n = n - 1-re és az összes x = x * 2-t x = x / 2-re cserélnénk, szomorú nyomtatási kép fogadna. 2^0 = 1 2^-1 = 0 2^-2 = 0 ... ami egyértelmûen helytelen. A baj okára magad is rájöhetsz: egész számok osztása során elvesznek a törtrészek és a 1/2 osztás 0.5 eredménye mint 0 tárolódik vissza x-be. Egészekkel ezt a problémát nem tudod megoldani, szükségünk lesz a Jáva egy másik alaptípusára, a lebegõpontos típusra. A lebegõpontos változót í gy deklarálod:

double pi; double z0,z1;

és így adhatsz neki értéket:

pi = 3.1415927;

A lebegõpontos szám már elég nagy ahhoz, hogy nagyobb számokat írjál le vele, mint amit kényelmesen be tudsz csépelni egy sorba, ezért jól jöhet a tudományos számábrázolás. Ha emlékszel, ekkor a számot egy 0-10 közötti szám és egy 10 hatvány szorzataként írjuk le. Pl: 2500 = 2.5*1000 = 2.5*103. Jávában ezt úgy kell leírni: 2.5E3. Tehát:

z0 = 2.5e3;

Persze ha nem akarod ezt a kis számot exponenciális ábrázolásban leírni, nem kell.

z1 = 2500;

A double-ban leírható maximális szám kb. 10308 , a legkisebb 10-308, ugyanez persze negatí v irányban is. Ekkora számokkal a gép persze jóval lassabban számol, mint egészekkel, í gy double-t csak akkor használjunk, ha szükséges.

Page 13: Bevezetes a Java Programozasba

Feladat

Í rjuk meg most az elõzõ, balsikerû feladványunkat a kettõ negatí v hatványaival úgy, hogy x-et egész helyett double-nak deklaráljuk. Próbáld meg magad megírni, majd ellenõrizd a megoldást .

Ennyit elsõ közelítésben a számokról és a velük való mûveletekrõl. A Jáva persze jóval több adattípussal és mûvelettel rendelkezik, mint amit eddig láttunk, de a következõ részben inkább a program végrehajtását befolyásoló szerkezetekkel foglalkozunk.

Ellenõrzõ kérdések

I. Mi az összefüggés a változó neve és értéke között?

II. Hogy jön a fiókos szekrény a számítógéphez?

III. Mit jelent, hogy egy változót deklarálni kell?

IV. Hol kell lennie egy változó deklarációjának?

V. Hogyan kell leírni egy egész típusú változó deklarációját?

VI. Mi a legnagyobb és legkisebb érték, amit egy egész típusú változóban tárolhatunk?

VII. Mi a különbség a Jáva értékadó utasítása és a matematikai egyenlõség között?

VIII. Mi az algoritmus?

IX. Hogyan kell a zárójeleket elhelyezni a 5+6*3 kifejezésben, hogy a kifejezés értéke 33 legyen? És ha 23-at akarunk?

X. Mi a lebegõpontos típus?

XI. Mekkora a legnagyobb és legkisebb szám, ami a lebegõpontos típussal leírható?

XII. Mi az exponenciális számábrázolás?

XIII. Mennyi 2.001e3?

XIV. Í rd le 0.12-t exponenciális ábrázolásban!

3. fejezet Terelgetjük a számítógépet; a program futásának menete. Struktúrált programozás, építkezés Matrjoska babákból. Elágazások és logikai kifejezések. Megdolgoztatjuk a gépet: a ciklusok.

A számítógép, mint mondtam, akár százmillió vagy újabban akár egymilliárd utasí tást is képes végrehajtani másodpercenként. A Jáva ugyan nagyban lassít rajta, azonban egy modern számítógép még a Jáva bájtkódot is jócskán másodpercenként tí zmillió utasí tás felett hajtja végre. Ekkora

Page 14: Bevezetes a Java Programozasba

sebességgel száguldó vonatot terelgetni kell, nehogy valami falnak ütközzön. Ezt a feladatot látják el a programvezérlõ utasí tások.

A programvezérlõ utasí tásokkal feltétellel vagy feltétel nélkül a program egy másik pontjára adhatjuk a vezérlést; a feltétel valamilyen kifejezés kiértékelésébõl adódik. Klasszikusan ezt a C vagy a BASIC goto utasításával egyszerû bemutatni, ez az utasí tás egy paramétert kap, amely paraméter egy helyet határoz meg a programban (egy sorszámot vagy egy cí mkét pédául). Amikor a goto végrehajtódik, a program futása a paraméter által meghatározott helyen folytatódik.

Jávában nincsen goto és ez egy vallás elterjedésének köszönhetõ. A strukturált programozás papjai a 70-es évek elején kezdték terjeszteni, hogy nem szép dolog a programot teleszórni goto-val, mert a végrehajtás nagyban követhetetlen lesz. Ez í gy is van. Az egyedül üdvözítõ megoldás a vallás hí vei szerint az egymást teljesen magába foglaló, struktúrált programblokkok rendszere. Egy ilyen programblokk egy vagy több (vagy sok) Jáva utasí tást foglal magába és késõbb fogjuk meglátni, mit lehet velük csinálni. Elõzetesként: feltételtõl függõen végre lehet hajtani a blokkot (vagyis a benne levõ utasí tásokat), ismételni lehet a blokkot, egy feltételtõl függõen újra végre lehet hajtani, stb. Akárhogy is, struktúrált a program attól lesz, hogy a blokkok határai nem keresztezik egymást, a fentebb levõ blokk teljesen magában foglalja a benne levõ blokkot vagy blokkokat. A Jávában a programblokkot egy { jel vezeti be és egy } jel zárja. Megjegyzendõ, hogy egy utasí tás önmagában blokkot képez, tehát ha a kapcsos zárójelek elmaradnak, a blokk csupán egy utasí tásból áll. Természetesen nem hiba egy utasí tást kapcsos zárójelek közé fogni, csak nem szükséges. Legegyszerûbb ezt egy példával megvilágítani!

Az if utasításnak van egy paramétere. A paraméter kiértékelésétõl függõen a mögötte levõ blokkot végrehajtja vagy sem. Példa:

if( x < 0 ) { System.out.println( "x kisebb volt, mint 0!" ); }

Minthogy a blokk csak egy utasí tásból áll, a kapcsos zárójelek elhagyhatók.

if( x < 0 ) System.out.println( "x kisebb volt, mint 0!" );

A paraméter egy új típusú, logikai kifejezés. Ha összeadunk két egész számot, az eredmény egy újabb egész lesz. Ha összehasonlítasz két számot, az eredmény logikai érték lesz. Ez egy teljesen szokásos adattípus, a neve boolean és összesen két értéke lehet, true és false. Mondhatod tehát ezt:

boolean f; f = true;

Sõt mondhatod ezt:

int x,y; boolean f; x = 0; y = 1; f = x < y;

Page 15: Bevezetes a Java Programozasba

A logikai kifejezések és operátorok tehát pont úgy viselkednek mint bármilyen kifejezés és értékül is adhatók logikai típusú változóknak. A következõ logikai mûveletek vannak két egész vagy lebegõpontos kifejezés között.

• x < y : true az eredmény, ha x kisebb, mint y, különben false • x > y : true az eredmény, ha x nagyobb, mint y, különben false • x == y : true az eredmény, ha x egyenlõ y-nal • x != y : true az eredmény, ha x nem egyenlõ y-nal • x <= y : true ha x kisebb vagy egyenlõ y-nal • x >= y : true, ha x nagyobb vagy egyenlõ y-nal

Vannak operátorok, amik már eleve logikai értékeken dolgoznak. Ezek:

• x && y : true akkor és csak akkor ha x true és y is true • x || y : true akkor, ha x vagy y közül valamelyik, esetleg mindkettõ true • !x : true, ha x false és viszont.

Nézzünk akkor egy jópofa logikai kifejezést feltételezve, hogy van x és y egész típusú változónk:

( ( x > -5 ) && ( x < 5 ) ) || ( ( y > -10 ) && ( y < 10 ) )

Ez a kifejezés akkor ad true értéket, ha x -5 és 5 közé esik vagy y 10 és -10 közé esik, esetleg mindkettõ igaz.

De térjünk vissza oda, ahonnan elindultunk, az if-hez. Az if elsõ paramétere egy logikai kifejezés, második egy programblokk, amit a kifejezés értékétõl függõen végre kell hajtani vagy átugrani. Lássunk egy praktikus példát és írjunk egy programot, ami kiírja, ha a program paramétere negatív. Ilyet még nem láttunk és most nem is fogok belemenni a részletes magyarázatába, legyen elég annyi, hogy át lehet venni a paramétereket, amivel a programot a parancssorban elindították.

public class Negativ { public void prog( String args[] ) { int x; try { x = Integer.parseInt( args[0] ); } catch( NumberFormatException ex ) { System.out.println( args[0]+" nem kiertekelheto" ); return; } if( x < 0 ) { System.out.println( x+" negativ" ); } } public static void main( String args[] ) { Negativ e = new Negativ(); e.prog( args ); } }

Ezt a programot, mint írtam, parancssori paraméterrel kell futtatni, különben hibaüzenetet kapsz (próbáld ki!). Próbáld ki továbbá különbözõ paraméterekkel.

Page 16: Bevezetes a Java Programozasba

java Negativ 5 java Negativ -10 java Negativ 0 java Negativ -29.34 java Negativ 2.3e12

Az utolsó két paraméter hatására hibaüzenetet kapunk. Ennek oka, hogy a számot egészként értékeltük ki és a két utolsó paraméterrel ezt nem lehet megtenni.

Programunknak van egy barátságtalan vonása: ha pozití v számot vagy 0-t kap, nem ír ki semmit. Szüntessük ezt meg! Megoldhatnánk a feladatot úgy, hogy a két kií rást két if-hez kötnénk pl. í gy:

if( x < 0 ) { System.out.println( "Negativ" ); } if( x >= 0 ) { System.out.println( "Pozitiv vagy 0" ); }

Nem szép, ugye? Nehéz is karbantartani, ha netán megváltoztatnánk a feltételt, a másikat sem szabad elfelejtenünk megváltoztatni. Sokkal szebben megoldható a feladat az if-hez opcionálisan kapcsolható else-ág segítségével. Ha az if utasításnak van else ága, az else mögött levõ blokk akkor hajtódik végre, ha az if feltétele hamis volt. Lássuk az elõzõ példát!

if( x < 0 ) { System.out.println( "Negativ" ); } else { System.out.println( "Pozitiv vagy 0" ); }

Feladat

Í rj egy olyan programot, ami egész paraméterérõl kiírja, pozitív, negatív, vagy nulla-e. Itt a megoldás ellenõrzésképpen.

Ha emlékszel a 2. fejezetre, volt ott egy olyan feladatunk, ahol 2 elsõ 8 hatványát írattuk ki. Most mi lenne, ha az elsõ 16 hatványra volnánk kiváncsiak? Abban a példaprogramban egy újabb hatványhoz egyszerûen megismételtük a szükséges sorokat. Ha a számítógépet el tudjuk terelni az egyenes vonalú végrehajtástól, akkor megspórolhatunk jó sok gépelést, ha megismételtetjük vele a programblokkot. Ezt Jávában is meg tudjuk csinálni a while utasítás segítségével. A while paramétere egy logikai kifejezés és ha ez a kifejezés igaz, a while végrehajtja a mögötte levõ programblokkot. Ezek után a gép újra kiértékeli a kifejezést, újra végrehajtja a blokkot és ez így megy addig, amíg a feltétel hamissá nem válik. Ha a feltétel már a legelsõ végrehajtásnál hamis volt, a programblokk egyszer sem hajtódik végre. A feltétel vizsgálata tehát a programblokk - a ciklusmag - elõtt van, ezért ezt a ciklust elöltesztelõnek hí vjuk. A következõ kis példaprogram kiírja a számokat 1-tõl 20-ig.

int n; n = 1;

Page 17: Bevezetes a Java Programozasba

while( n <= 20 ) { System.out.println( n ); n = n+ 1; }

Feladat

Csinálj a fenti kis programrészletbõl végrehajtható programot, tehát tedd köré mindazt, amitõl Jáva programmá válik. Ellenõrizd a megoldást itt.

Feladat

Most csináld meg azt a programot, ami 2 elsõ 16 hatványát. Használd fel a 2. fejezet programját, ami az elsõ 8 hatványt írta ki a számító rutin ismételgetésével és szervezd ciklusba, amit akkor egyszerû kódismétléssel oldottunk meg. A megoldást itt ellenõrizheted.

A ciklusok remek dolgok, egy rendes program tartalmaz belõlük jócskán. A while ciklusokkal minden feladat megoldható, de nem mindig áll kézre. A while-nak mindenekelõtt van egy hátultesztelõ változata, a do-while. Ez ugyanazt tudja, mint a while, azzal a különbséggel, hogy az ellenõrzés a ciklus végén történik, tehát a ciklusmag egyszer mindenképpen végrehajtódik. Példa:

int n; n = 1; do { System.out.println( n ); n = n+1; } while( n <= 20 );

A másik fontos szerkezet a fentiekben bemutatott szerkezet zanzásított változata, amivel sok í rásmunka megspórolható. A for ciklus magába foglalja a ciklusváltozó inicializálását, a ciklusfeltétel ellenõrzését (elõltesztelõ formában) és a ciklusváltozó megváltoztatását. Az általános forma a következõ:

for( ciklusváltozó inicializálás ; feltétel ellenõrzés ; ciklusváltozó módosítás ) ciklusmag

Elõzõ számolós példánkat a következõképpen tudod for ciklusba átírni.

int n; for( n = 0 ; n <= 20 ; n = n + 1 ) System.out.println( n );

Fontos megjegyezni, hogy a for fejének mindhárom része opcionális, ha elmarad, akkor a ciklusutasí tás maga nem végzi el az adott mûveletet. Ez lehetõvé teszi számunkra, hogy pl. a ciklusváltozó növelését a ciklus belsejében hajtsuk végre, ha az olyan bonyolult, hogy egy utasí tásban nehéz leírni. Ad abszurdum a következõ program értelmes:

for( ; ; ) System.out.println( "Hello!" );

Page 18: Bevezetes a Java Programozasba

Ez a programrész végtelen ciklusban fogja az üzenetet kiírni. Nincs inicializálás és nincs ciklusváltozó-növelés valamint a ciklusfeltétel elmaradása azt jelenti, hogy a feltételt nem kell ellenõrizni, hanem feltétel nélkül folytatni a ciklust.

Feladat

Elõzõ feladatunkat a 2 elsõ 16 hatványáról írd át for ciklusos változatba! Ha megírtad és mûködik, ellenõrizd a megoldást itt!

Feladat

Í rj egy programot, ami kinyomtatja a kisegyszeregyet! Használd fel a System.out.println rokonát, a System.out.print-et, ami nem tesz soremelést a kinyomtatott sor végére, í gy több eredményt nyomtathatsz egy sorba. A szorzótábla kinyomtatásához használj két for hurkot egymás hasában, a belsõ hurok nyomtat egy sort, a külsõ ciklus ismételgeti a sornyomtató ciklust. Küzdj meg a programmal, majd ellenõrizd az eredményt! Figyeld meg, a külsõ ciklus magját kapcsos zárójelek fogják közre, mert az két utasí tásból áll, a belsõ for-ból és az üres szöveget (plusz egy soremelést) nyomtató System.out.println-bõl. A belsõ ciklus magja csak egy utasí tást, a System.out.print-et tartalmaz, í gy ott a kapcsos zárójelek nem szükségesek (bár ottlétük nem lenne hiba). Figyeld meg a struktúrált programozást: a külsõ ciklus teljesen magába foglalja a belsõt!

Ellenõrzõ kérdések

I. Mit jelent a programblokk?

II. Mit jelent, hogy a programblokkok struktúráltak?

III. Hogyan kell egy programblokkot leírni Jávában?

IV. Programblokkot alkothat-e egy Jáva utasí tás?

V. Mit jelent a logikai kifejezés?

VI. Hogyan kell logikai típusú változókat deklarálni?

VII. Mit csinál az != operátor?

VIII. Mit csinál a || operátor?

IX. Mi az if utasí tás

X. Mit jelent, ha az if utasí tásnak else ága van?

XI. Mi a while utasí tás?

XII. Mi a ciklusmag?

XIII. Mit jelent, hogy a while elöltesztelõ ciklus?

XIV. Hogyan lehet hátultesztelõ while ciklust írni?

Page 19: Bevezetes a Java Programozasba

XV. Mi a for ciklus?

XVI. Elöl- vagy hátultesztelõ a for ciklus?

XVII. Mit jelent az egymásba ágyazott ciklus?

4. fejezet Megjegyzések. Írni utálunk, ezért törekszünk az újra felhasználható programrészekre. Függvények a matematikában és Jávában. Paraméterek, visszatérési értékek és változó láthatósági szabályok.

Programjaink most már kezdenek kellõen bonyolultak lenni ahhoz, hogy magyarázat nélkül ne értsük õket. Minden programozási nyelv, köztük a Jáva is lehetõséget ad arra, hogy ember számára értelmes magyarázó szövegeket, megjegyzéseket tegyünk a kódba. A megjegyzéseket a számítógép egyszerûen figyelmen kí vül hagyja, semmilyen hatása nincsen a program mûködésére. Nem is neki szólnak, hanem a program olvasójának. Jávában kétfajta megjegyzés van. Az egyik /* karakterekkel kezdõdik és a legközelebbi */ karakterekig tart, a programban bárhol állhat, több soros is lehet. Pl.

/* Itt egy megjegyzés */ i = 1; /* A sor végén egy megjegyzés */ /* Hosszú, több sort átfogó megjegyzés */ i /* a változó, amit használunk */ = /* értékadó utasítás */ 2 /* 2 egy jó érték */;

Az utolsó variációt nem kell követendõ példának tekintened, csak azt akartam vele megmutatni, hogy ez a fajta megjegyzés tényleg mindenhol lehet. A másikfajta megjegyzést // jelek vezetik be és a sor végéig tart. Kevesebbet kell írni vele, rövid megjegyzéseknél ezért szeretik. Példák:

// Egy rövid kis megjegyzés i = 3; // Végül is igazában 3-at szeretnénk i-be // Ha hosszú megjegyzést akarunk, // Minden sor elejére kell a // // jel.

Feladat

Í rjunk egy programot, ami kiírja az y=x2 függvény értékeit x=(-1,1) intervallumban, 0.1-es lépésenként. Megjegyzésekkel tedd világosabbá, mikor mi történik! A feladat nem okozhat nehézséget, azért a biztonság kedvéért ellenõrizd a megoldást!

Page 20: Bevezetes a Java Programozasba

A megoldásban felhasználtunk egy újdonságot, a += operátort. Ez az operátor hozzáadja a bal oldalán levõ változóhoz a jobb oldalán levõ értéket, tehát kiveszi a változó értékét, hozzáadja az operátor jobb oldalán levõ kifejezés értékét és visszatárolja a változóba.

x += 0.1;

tehát egyenértékû

x = x + 0.1;

kifejezéssel.

A kií rásban furcsa eredményeket találhattál, pl. í me egy részlet: ... -0.20000000000000015^2=0.04000000000000006 -0.10000000000000014^2=0.01000000000000003 -1.3877787807814457E-16^2=1.9259299443872359E-32 0.09999999999999987^2=0.009999999999999974 ...

Nagyjából stimmel, de mik ezek a furcsa értékek a végén? És pláne, miért nem kapunk 0-t (habár 10-

16 elég közel van hozzá ...)? Tudnod kell, hogy a számítógép számára csak az egész, int értékek pontosak. Mivel a gép másként tárolja a számokat, mint az ember, nem biztos, hogy az olyan szép, kerek értékek, mint a 0.1 a gép számára is ugyanolyan szépek. A 0.1 pont egy olyan szám, amit a gép nem tud teljesen pontosan ábrázolni, ezért az összeadások során apró hibák halmozódnak fel. Éppen ez okból a lebegõpontos számokat sose vizsgáljuk egyenlõségre, mert könnyen lehet, hogy az egyenlõség sohase teljesül. Esetünkben például x sohase lesz pontosan 0 vagy 1, ezért a furcsa határértékösszehasonlí tás a for ciklusban. Hadd ismételjem meg, hogy ez a probléma csak a lebegõpontos számoknál léphet fel, egészeknél soha, ez egy újabb ok arra, hogy mindenhol egészeket használjunk, ahol csak lehet.

Akárhogy is, nem erre akartam kilyukadni. Maga a kiszámítandó függvény a program közepében van, ha cserélni akarnánk, ki kellene keresnünk a programszövegbõl. Rosszabb, ha a függvény netán bonyolultabb lenne és nem lehetne egy sorban kiszámolni, netán esetleg belsõ változókat használna, a kiszámítandó függvény kódja teljesen összekeveredne a táblázatnyomtató program kódjával. Ezen okból a Jáva rendelkezésünkre bocsátja a saját függvények definiálásának lehetõségét. Igazából már van két saját függvényünk, de azok vadító kék színben tündököltek. Lássuk elõzõ programunk módosítását olymódon, hogy az y = x2 számítását végzõ részt kiemeljük saját függvénybe!

public class x2ertek_2 { double fuggveny( double xi ) { double yi; yi = xi * xi; return yi; } public void prog( String args[] ) { double x; for( x = -1 ; x < 1.001 ; x += 0.1 ) { System.out.println( x+"^2="+fuggveny( x )); } }

Page 21: Bevezetes a Java Programozasba

public static void main( String args[] ) { x2ertek_2 e = new x2ertek_2(); e.prog( args ); } }

Ha a programot lefuttatjuk, persze ugyanazt az eredményt kapjuk, mint az elõzõ példában. Mik akkor a változások? Vegyük mindenekelõtt észre a kék sorok drasztikus megfogyatkozását! Vegyük továbbá észre, hogy a táblázatnyomtató ciklusunkban könnyed eleganciával kijelentettük, hogy fuggveny( x ) által visszaadott értéket is ki akarjuk írni. Ez egy függvényhívás.A függvényhí vás során a számítógép egy alprogram végrehajtásával folytatja munkáját. Az alprogramot valahol lezárják egy visszatérõ utasí tással és megmondják, mi az érték, amit az alprogram visszaad. Ekkor a számítógép elõkeresi, hol hagyta abba a fõprogram végrehajtását és a függvényhí vást helyettesíti azzal az értékkel, amit az alprogram visszaadott. A függvény tehát bármilyen kifejezésben állhat, ahol a függvény visszatérési értékének típusa megegyezik az adott kifejezésben várt adattípussal. Például ha van egy függvényünk, ami double-t ad vissza, akkor az felhasználható double számokkal dolgozó kifejezésben.

double fuggv() { ... } ...

double x; x = 3.14*fuggv()+2.5;

Maga a függvény deklarációja bárhol lehet a programban, a függvényhí vás elõtt és után is. Tilos azonban ugyanazt a függvényt kétszer deklarálni ugyanazzal az aláírással vagyis ugyanazzal a névvel, visszatérési értékkel és paraméterlistával. Azonos nevû, de különbözõ aláí rású függvények (tehát például ahol az egyiknek nincs paramétere, a másiknak pedig van) különbözõnek számítanak, mert a Jáva a függvényhí vás alapján rájön, hogy melyik függvényt kell meghí vnia. Példa:

// Elsõ függvény int fuggv() { ... } // Második függvény int fuggv( int i ) { ... } int i = fuggv(); // Ez az elsõ függvényt hívja meg int k = fuggv( 2 ); // Ez a második függvényt hívja meg

A függvénynek lehet egy speciális visszatérési értéke, a void. Ez azt jelenti, hogy a függvény nem ad vissza értéket, az ilyen függvényt nem lehet kifejezésben felhasználni, de kifejezésen kí vül meg lehet hí vni. Pont ezen a módon kell alprogramokat, procedurákat definiálni.

void fuggv2() { ...

Page 22: Bevezetes a Java Programozasba

} ...

fuggv2();

A függvényeknek lehetnek bemeneti paramétereik is. Ezek azok az értékek, amelyeket átadunk az alprogramnak, hogy számoljon belõle kimeneti értéket. A bemeneti paramétereket a függvényfejben soroljuk fel.

double fuggv3( double elso_param, int masodik_param ) { double eredm; ... return eredm; }

... double eredm; eredm = fuggv3( 3.14, 4 );

Amikor a függvényt meghí vod, bonyolult dolgok játszódnak le.

I. Kiértékelõdnek a függvény meghí vásakor a függvénynek átadott kifejezések

II. Új kontextus jön létre a változóknak, az eddigi használt változókat nem lehet többet elérni. Ezt késõbb majd pontosítjuk (vannak olyan változók, amelyek túlélik a függvényhí vást), de most jegyezzük meg inkább úgy, hogy az eddig deklarált változók a függvény futása alatt nem látszanak. Megmaradnak, nem vesznek el, de a függvény nem látja õket, mert õket egy másik kontextusban deklarálták.

III. A függvénynek átadott paraméterek megfelelõ sorrendben hozzárendelõdnek a bemeneti paraméterekhez. Valójában az történik, hogy a bemeneti paraméterek mint változók létrejönnek a függvény saját kontextusában és kezdõértékül megkapják a hí vási értékeket. A példánkban tehát az elso_param nevû változó értéke 3.14 lesz, a masodik_param-é pedig 4.

IV. A függvény futni kezd és úgy viselkedik, mint bármely Jáva program - teheti, mert valójában õ is teljes jogú program, aminek saját környezete van. Saját változókat deklarálhat, más függvényeket és procedúrákat hí vhat meg. Ámde eljön egyszer az igazság pillanata és befejezi a futását. Ez kétféleképpen történhet. Elõször is elérhet egy return utasítást. A return mögött a függvény visszatérési értékének megfelelõ típusú kifejezés vagy - void visszatérési érték esetén - semmi sem található. A return hatására a függvény befejezi a futását és visszatérünk a fõprogramba. Második lehetõség csak és kizárólag void visszatérési értékû függvényeknél fordulhat elõ: ilyenkor egyszerûen elérhetjük a függvényt lezáró kapcsos zárójelt és ezt a gép magától, mint egy üres return; utasí tást értelmezi.

V. Ha a függvény futása befejezõdik, a visszatérési értéket a gép biztos helyre menti, majd egész egyszerûen felszámolja az összes változót, amit a függvény kontextusában deklaráltak, ezek törlõdnek és örökre elvesznek.

VI. A függvényt hí vó program kontextusa visszaállítódik, változói láthatóvá válnak.

Page 23: Bevezetes a Java Programozasba

VII. A gép most elõveszi a biztos helyrõl a függvény visszatérési értékét és mintha mi sem történt volna, folytatja a kifejezés kiértékelését, amiben a függvényhí vás volt. Az egész függvényhí vás egy darab számmá, a függvény visszatérési értékévé zsugorodott össze és ezzel a számmal helyettesítõdik.

Hasonlatos ez ahhoz, mint amikor a cikkedhez ki akarsz számolni egy értéket, de csak a szám érdekel. Néhány sajtpapíron elkészíted a számításokat, a számot beírod a cikkbe, majd a sajtpaírokat eldobod. Í gy mûködik a függvény is, a részeredmények érdektelenek a függvény lefutása után és ami még fontosabb, a függvény belsõ ügyei nem zavarják a program futását.

A függvénybõl természetesen újabb függvényt is meghí vhatsz és a hí vásra ugyanazok a szabályok vonatkoznak. Ezt nem csinálhatod a végtelenségig, valamekkora korláta van annak, milyen mélységben hivogathatják a függvények egymást, de ez sok ezres mélység.

Lássuk, megértetted-e!

Feladat

Olvasd el az itt látható programot és mondd meg nekem, mi a nyomtatási kép! Utána próbáld is ki és találd ki az okát, ha netán tévedtél volna. Segítség: nézd meg, melyik függvényhí vás melyik fuggv függvényt hí vja meg. Ahol a függvényhí vásban paramétert adunk át, ott a paraméterrel rendelkezõ fuggv fut, ahol nem, ott a paraméter nélküli.

A megjegyzések használata persze magánügy, de talán a fenti példa is demonstrálja, hogy legalább a függvények fejét (tehát a deklarációjának az elejét, a visszatérési értékének, nevének és paramétereinek megadását) mindenképpen ajánlott megjegyzéssel ellátni, milyen paramétert vár a függvény, azokkal mit csinál és mit ad vissza.

Feladat

Bergengóciában az adórendszert lényegesen leegyszerûsítették és a Totális Adótábla XXVIII. lapja a következõképpen néz ki:

-100 fbtk* a jövedelem 9%-a 100-250 9+a 100 fbtk fölötti rész 13%-a 250-750 30+a 250 fbtk fölötti rész 25%-a 750- 180+a 750 fbtk fölötti rész 50%-a *- fbtk: konvertibilis fabatka, éves szinten

Az utolsó pillanatban a Tüntetõ Bergengóc Anyák Antidemokratikus Szövetségének sikerült keresztülvinnie követeléseit és a családosokoknak kedvezõ intézkedésekkel egyszerûsítették a táblát. Ezek szerint a 1 gyerek nevelése esetén az adó 2%-át, kettõ esetén 8%-át, 3 és a felett gyerekenként 4%-át, de legfeljebb 25%-ot engednek el. Í rjunk most egy programot, amit meghí vhatunk az éves jövedelemmel és a gyerekek számával és az megmondja az adónkat! Emlékezzünk a 3. lecke Negativ programjára, hogyan értékeltük ki ott a paramétereket! Lebegõpontos számot még nem értékeltünk ki, ezért leshetsz egy kicsit a megoldásban. A programunkban igyekezzünk olyan függvényt írni, ami a leginkább lerövidíti a programot! Í me a megoldás, de csak a paraméterek kiértékelését lesheted ki belõle, a többit neked kell megoldanod!

Page 24: Bevezetes a Java Programozasba

Ellenõrzõ kérdések

I. Mire szolgálnak a megjegyzések a programban?

II. Mi a különbség a /* */ és a // megjegyzésszintaktika között?

III. Miért nem vizsgáljuk a lebegõpontos számokat egyenlõségre?

IV. Mi a függvény aláí rása?

V. Mi a void típus?

VI. Mik játszódnak le egy függvényhí váskor?

VII. Hogyan adja vissza a függvény a visszatérési értékét?

VIII. Mi történik a függvény által deklarált változókkal?

IX. Mi történik, ha a hí vó függvény és a hí vott függvény megegyezõ nevû változókat használ? Milyen mélységben hí vhatják függvények egymást?

5. fejezet Matematikai függvények, a Jáva matematikai függvényei és azok használata.Egyszerû matematikai problémák programnyelvi megoldásai. Feladatok a függvények használatára.

Mint már említettük, a számítógép másodpercenként több milliárd gépi szintû mûvelet elvégzésére képes, míg függvényhívásokból is képes több tízezer elvégzésére. Megfogalmazódik bennünk a kérdés, hogy miként is lehetne ezt ésszerûen használni olyan problémák megoldására, amelyek a mindennapi életben is elôfordulhatnak. A számítógép igazi ereje akkor mutatkozik meg, ha olyan algoritmus (elemi lépések véges számú, jól meghatározott sorozata) végrehajtására utasítjuk, mely nekünk hosszas papíron történô számolást igényelne. A négy alapmûveletet kiterjesztendô, ismerkedjünk meg a hatványozás, gyökvonás, az exponenciális és trigonometrikusfüggvények Java nyelvi megfelelôivel.

A következô program segítségével hatványozást és gyökvonást végezhetünk, melyek a java.lang csomag Math osztályában találhatók.

A hatványozás függvénye: double pow(double alap, double kitevo) A négyzetgyökvonás: double sqrt(double alap)

Nézzük a programot! Math1 public class math1 { public double Hatvany(double x, double n){ return Math.pow(x,n); //A beépített hatványfv. Meghívása }

Page 25: Bevezetes a Java Programozasba

public double Negyzetgyok(double y){ return Math.sqrt(y); //A beépített gyökfv. meghívása } public void prog( String args[] ) { System.out.println("Példa a hatványozás és gyökvonáshasználatára:"); double alap, kitevo; //Változót deklarálhatunk itt is /* A fôprogram elsô két paramétere lesz a hatványfüggvényben az alap és a kitevôértéke. A 3.-nak megadott paramétert pedig a gyökfüggvény kapja értékül.*/ alap=Double.parseDouble(args[0]); kitevo=Double.parseDouble(args[1]); /* A hatványfv double értékeket vár, amit az értékadásnál így közlünk. */ System.out.println(args[0]+"^"+args[1]+"="+Math.round(Hatvany(alap, kitevo))); //Math.round: a double értéket egész számként íratjuk ki. int gyokalap; gyokalap=Integer.parseInt(args[2]); System.out.println(args[2]+"Negyzetgyoke="+Negyzetgyok(gyokalap ) ); } public static void main( String args[] ) { math1 e = new math1(); e.prog( args ); } } Megjegyzések:

I. A Hatvany és a Negyzetgyok függvények double paramétereket fogadnakés adnak is vissza, a függvénytörzsben meghí vják a Math osztály megfelelô (pow és sqrt) függvényeit.

II. Az általánosí tás kedvéért az args[] segítségével töltjük fel függvényeinket paraméterekkel. Í gy minden futtatáskor változtathatjuk a bemeneti paraméterek értékeit.Az args[] használatát szintén egy késôbbi fejezetben magyarázzuk részletesen, legyen elég annyi, hogy a programnak a parancssorban megadott 1.,2.,3. stb. paraméter rendre az args[0],args[1], args[2] változók tárolják.

III. Az alap éskitevo double típusú változókrendre a fôprogram elsô két paraméterét kapják.Ahhoz, hogy a program az általunk megadott értékeket double-ként kezelje, Double.parseDouble() függvényt használjuk. Hasonlóképpen, a gyokalap változó a harmadik fôprogram paraméter lesz, integer-ként értelmezve.

IV. A hatványfüggvény double értéketad vissza, amit a Math.round() függvénnyel alsó egészértéket kapunk vissza.

Fordítás után a futtatáshoz adjuk ki az alábbi parancsot: java math1 1.5 2 8

A futás eredménye:

Page 26: Bevezetes a Java Programozasba

Példa a hatványozás és gyökvonás használatára: 1.5^2=2 8 Négyzetgyöke=2.8284271247461903

A hatványozásnál láthatjuk, hogy a 2.25 helyett a kerekített értéket kapjuk, míg a 8 négyzetgyökét double alakban, 16 tizedesjegy pontossággal.

Az alábbitáblázatban foglaljuk össze a Math osztály legfontosabb függvényeit:

Visszatérési érték típusa

Függvényfej Funkció

Double abs(double a) Abszolút érték fv *

Double cos/sin/tan/asin/acos/atan(double) Trigonometrikus fvek és inverzeik

Double Exp/log(double) és ln(x)

Double toDegrees/toRadians(double) Radián érték fok-ba váltása és fordítva

A trigonometrikus függvények használatához szükségünk lehet a matematika középiskolából jól ismert két nevezetes konstansára: e-re és pi-re. Ezeket a Math osztály speciális változóiként kell kezelnünk, matematikai szempontból az y=e és y=pi konstansfüggvények felelnek meg:

public static final double PI

public static final double E

Arra, hogy ezek konstans értékû változók, arra a final jelzô utal (értékük nem változik). Használatuk a program szövegében: Math.PI illetve Math.E hivatkozással történik, vagyis meg kellmondanunk expliciten, hogy a Math osztály konstans értékûváltozóiról van szó.

1. Feladat Egyszerû gyakorlatként írjuk át a kör kerületének és területének kiszámítását elvégzô programunkat a Math.PI használatával! Az egyszerûsített megoldást itt ellenôrizheted.

2. Feladat Készítsünk függvénytáblázatot, mely kiírja a nevezetesebb hegyesszögek (0, 30, 45, 60, 90) sin, cos,tan, ctg értékeit! A megoldást itt ellenôrizd.

3. Feladat Készítsünk programot, mely megoldja a másodfokú egyenletet, a megoldhatóság teljes vizsgálatával.

A fôprogram 3 bemeneti paramétere az másodfokú egyenlet a, b , c paramétere. A megoldást itt találhatod.

A Math függvények közt találhatunk egy véletlen számokat generáló függvényt is: double random ()

Page 27: Bevezetes a Java Programozasba

A függvény aláí rásából láthatjuk.hogy paraméter nélküli és double értéket advissza, mégpedig egy 0<=x<1 pszeudo-véletlenszámot generál. (A pszeudo jelzés arra utal, hogy nem kaphatunk tökéletes véletlenszerûséget,hiszen a generált szám valamilyen módon függ a pocesszorórajelétôl.) A függvény használata elég egyszerû: ha az [a;b] intervallumba esô véletlen számokat szeretnénk generálni, akkor nem kell mást tennünk, mint venni az a+(b-a+1)*Math.random() kifejezést.

4. Feladat Készítsünk 5-ös és 6-os lottó programot a Math osztály véletlenszám generátorának segítségével! Elôször írjuk meg úgy, hogy ne legyen benne ellenôrzés, nem fordul-e elô kétszer ugyanaz a lottószám. A megoldást itt ellenôrizheted! Ezek után oldjuk meg, hogy a számok garantáltan különbözôek legyenek. Itt a megoldás ellenôrzésképpen.

5. Feladat* Készítsünk totó programot a Math osztály véletlenszám generátorának segítségével! A program elkészítéséhez ismerjük meg és használjuk a % operátort! A % ugyanúgy mûködik, mint az osztás (/), de a hányados helyett a maradékot eredm´ nyezi. Egészre és lebegôpontos számra is használható. A megoldást itt ellenôrizheted!

6. Feladat* Í rjunk programot, mely az Euklidészi algoritmus segítségével keresi meg meg két egész szám legnagyobb közös osztóját! Í me egy példa, hogyan mûködik az algoritmus. Keressük 60 és 22 legnagyobb közös osztóját. 60 = 2*22 + 16 22 = 1*16 + 6 16 = 2*6 + 4 6 = 1*4 + 2 4 = 2*2 + 0 2 a legnagyobb közös osztó, mivel ez volt az utolsó nem 0 maradék. A megoldást itt ellenôrizheted!

Ellenõrzõ kérdések

I. Mit nevezünk "beépített" függvényknek?

II. Hogyan tudunk a beépített függvényekre hivatkozni?

III. Hogyan tudunk trigonometriai feladatokat Java program segítségével megoldani?

IV. Mit nevezünk konstans értékû változónak? Mondj példát konstans értékû Java változóra!

V. Mire kell vigyáznunk a trigonometrikus Java függvények bemeneti paramétereinek megadásánál?

VI. Hogyan tudunk java programjainkban kerekített értékeket használni?

VII. Milyen lehetôségeink vannak a Java-ban véletlen számok generálására?

Page 28: Bevezetes a Java Programozasba

6. fejezet További gyakorló feladatok matematikai problémák megoldására. Cimke. Többszörös elágazás. Kilépés programblokkból: break. Kilépés ciklusból: continue. 2 ismeretlenes lineáris egyenletrendszerek megoldási módszerei és azok JAVA nyelvû megvalósítása. A mátrixelmélet elemei: mátrix, determináns. A 3- és többismeretlenes egyenletrendszerek megoldási lehetõségei és JAVA nyelvû megvalósítása.

Bármely java utasí tást azonosíthatunk úgynevezett címkékkel. Szerkezete: cimke: utasítás

Egyszerû példa egy ciklusból való kilépés való kilépés: kulso: for (int j=100; j>=10;j--){ for (int i=0; i<10;i++){ Math.pow(2,i)*j; if (i>5) break kulso; } } A fenti ciklus futása megszakad abban az esetben, ha (i>5).Nézzük mi is történik! Elôször is elhelyeztünk egy cimkét a küsõ ciklusfej elé, mely azonosítja a ciklust.Ezután a küsõ ciklust bizonyos feltétel fennállása esetén megszakítjuk, vagyis kilépünk belôle. Ez azt jelenti, hogy a vezérlés (végrehajtás) a külsõ ciklusmag utáni, tehát a ciklust követô utasí tásra kerül. A break utasítás, tehát arra szolgál, hogy egy ciklust, illetve egy programblokkot elhagyhassunk vele. 2 féle használata létezik: break; - ekkor a vezérlés a break-et tartalmazó utasí tásblokkból kilép. break cimke; - ekkor pedig a cimkével megjelölt blokkot hagyjuk el. Ha a fenti példában a break cimke nélkül állna, akkor csak a belsõ ciklusbõl lépnénk ki. Jól jegyezzük meg, hogy a break utasítás nem alkalmas függvénybôl (metódusból - lásd késôbb) vagy inicializáló blokkból való kilépésre.

A másik lehetôségünk egy ciklus normál menetének megváltoztatására a continue utasítás. Amennyiben continue szerepel a ciklusmagban egy feltétel után, akkor a feltétel teljesülése esetén a ciklusmagban lévô további utasí tások nem kerülnek végrehajtásra, a vezérlésa ciklusfejre kerül. Épp úgy, mint a break esetében,a continue-val sem lehet függvénybôl vagy inicializáló programblokkból kilépni.

Mit ír ki a képernyõre az alábbi programrészlet utolsó utasítása? int s=0; for (int i=0;i<=20;i++) { if (i>=10)&&(i<=14)) continue; s=s+i; } System.out.println(s);

Page 29: Bevezetes a Java Programozasba

A continue hasznos lehet, ha meg szeretnénk kí mélni magunkat attól, hogy bonyolult feltételeket írjunk a ciklusmagba.

Feladat

1. Készítsünk programot, mely 1-100-ig kiírja a 3-al, 5-el és 7-el nem osztható számokat. A ciklusmagban használjuk a continue utasí tást! Megoldás itt. Aki megnézte a másodfokú egyenlet megoldó programjának megoldását, az valószí nûleg bonyolultnak találta a sok 'if'-es szerkezetet. Ennek orvoslására a JAVA felkí nálja a switch-case szerkezetet. Ezzel az utasí tással töbszörös elágazást valósíthatunk meg egy egész értékû kifejezés értékei szerint.

Példaként tekintsük az alábbi programrészletet: void osztalyzat(int n){ switch (n){ case 1: System.out.println("Elegtelen"); break; case 2: System.out.println("Elegseges"); break; case 3: System.out.println("Kozepes"); break; case 4: System.out.println("Jo"); break; case 5: System.out.println("Jeles"); default: System.out.println("Gratulálok!"); } } Mi történik abban az esetben, ha n=5 és mi, ha n!=5? Amikor a case ág végén break szerepel, akkor a vezérlés kilép a switch utasí tásból; amennyiben nincs break, a végrehajtás a következô cimkén folytatódik. A default (alapértelmezett) ág használata opcionális. Ily módon, ha n=1, akkor a break miatt kilépünk a swith utasításból, míg, n=5 esetén break hiányában a deafult ág is végrehajtódik, vagyis gratulálunk a jeles osztályzathoz.

A teljes programot itt találod.

Feladat

2. a) Készítsünk programot, mely a fôprogram paraméterként kapott 1..7 közötti egész szám, mint sorszám függvényében kiírja szövegesen a hét megfelelô napját! Megoldás itt. b) Vegyük alapul a 2001. évet. Fejlesszük tovább az elõzõ programot úgy, hogy a fõprogram két bemenõ paraméterét, rendre a napot és hónapot, értékelje ki és modja meg, hogy az így megadott dátum a hét melyik napja. A megoldás itt található. Most készítsünk egy új változatot, ami ugyancsak a 2001 évre érvényes, egy hónapszámot vár paraméterül és kinyomtatja az adott hónap naptárát csinos formában, ahogy a zsebnaptárokon megszokott. Ellenôrizd a megoldást itt!

Page 30: Bevezetes a Java Programozasba

Egyenletrendszerek megoldása

A lineáris egyenletrendszerek elég fontos szerepet játszanak a természettudományokban, különösen a matematika néhány területén. Nem ár t megismernünk néhány megoldási módszert, s persze ennek JAVA megvalósí tását is. Középiskolában találkoztunk a 2 ismeretlenes lineáris egyenletrendszerrel.

I. a*x+b*y=p II. c*x+d*y=q ahol a,b,c,d, p, q az egyenlet (például valós) konstansai, mí g x1, x2 valós változók, az egyenletrendszer ismeretlenei.

Ennek megoldása nem jelenthet túl nagy gondot, hiszen a változó helyettesítés módszerével könnyen célt érünk. Kiválasztjuk az egyik egyenletet, az egyik ismeretlent kifejezzük, majd az í gy kifejezett ismeretlent behelyettesítjük a másik egyenletbe, mely í gy márcsak egy ismeretlent tartalmaz. Kiszámítva az ismeretlen konkrét értékét, azt visszahelyettesítve a másik, már kifejezett ismeretlent tartalmazó egyenletbe, kész vagyunk.

Példa:

Oldjuk meg a valós számok halmazán az alábbi egyenletrendszert!

I. 4x-5y=22

II. 7x+2y=17 --------------- II. y=(17-7x)/2

y-t I.-be: 4x-5*[(17-7x)/2]=22

I.. 4x-(85-35x)/2=22

I. 8x-85+35x=44

I. 43x=129 azaz x=3

II. y=(17-21)/2= -2

A másik, az egyenlô együtthatók módszere, amikor is a két egyenlet mindkét oldalát úgy szorzom meg konstans értékekkel, hogy az egyik ismeretlen együtthatói megegyezzenek. Ha például az x ismeretlen kiküszöbölése a célunk a fenti általános egyenletrendszerbôl, akkor az elsô egyenletet LKKT(a,c)/c-vel, mí g a második egyenletet LKKT(a,c)/a-val kell megszoroznunk, ahol a LKKT a két szám legkisebbközös többszöröse. [A LKKT-t legyszerûbben úgy számíthatjuk ki, ha a két szám szorzatát elosztom a legnagyobb közös osztójukkal (LNKO). Ha ez bonyolultnak tûnik, akkor megfelelô a c-vel, illetve a-val történô szorzásis.] Ezután az egyik egyenletbôl kivonom a másikat,saz í gy kapott egyenlet már csak 1 ismeretlent tartalmaz.

Nézzük meg az elôzô egyenletrendszer megoldását az egyenlô együtthatók módszerével is:

Page 31: Bevezetes a Java Programozasba

7* I.: 28x-35y=154 4*II: 28x+8y =68

I-II: -43y=86 azaz y=-2

II: 7x-4=17 azaz x=3

A két módszer egyenértékû, bármlyikkel dolgozhatunk. A megoldások vizsgálata során 2 esetre kell kitérnünk.

1. Létezik olyan eset, amikor a két egyenlet egymással ekvivalens. Ilyenkor az egyenletrendszer határozatlan, azaz az egyik ismeretlent tetszôleges t valós paraméternek választva, kifejezhetjük a másikat, vagyis ekkor végtelen sok megoldás létezik. 2. Lehetséges az is, hogy az egyenletrendszer megoldása során ellentmondásra jutunk (tipikusan akkor, ha az átalakí tások soárn azt tapasztaljuk, hogy a 2 egyenlet bal oldala egyenlô, mí g jobb oldaluk nem). Ekkor nincs megoldás.

Í me, egy egyszerû feltétel a határozatlanság, illetve ellentmondásosság vizsgálatára. Ha a fenti általános egyenletrendszerben: a*d-b*c==0 és 1) p*c==a*q, akkor az egyenletrendszer határozatlan; 2) p*c!= a*q, akkor az egyenletrendszer ellentmondásos.

Feladat

A fenti ismeretek értelmében írjunk programot, mely megold egy 2 ismeretlenes, lineáris egyenletrendszert a megoldási lehetôségek teljes vizsgálatával! A mogoldást itt találod.

A helyzet kicsit bonyolultabb 3 ismeretlenes egyenletrendszerek esetén.A megoldási módszerek ismeretéhez szükség van egy kis felsôbb 'matek'-ra. Az elsô fogalom, amit bevezetünk aza mártix. Egy mátrixot elég úgy elképzelnünk,mint egy n*m-esszámtáblázatot. A mátrix elemeire indexeléssel tudunk hivatkozni. Az a(i,j) elem a mátrix i. sorának j. oszpában lévô elemet jelenti. Középsikolában tanultuk a vektorfogalmát. Nos, a mátrix úgy is elképzelhetô, mintegy olyan sorvektor, melynek elemei oszlopvektorok vagy fordítva: olyan oszlopvektor, melynek elemei sorvektorok. Az alábbi példa egy 3*3-as mátrixot mutat:

Elnevezési konvenció, hogy a mátrixokat nagybetûvel, elemeit pedig az adott nagybetû indexelt kisbetûivel jelöljük. Ha a fenti mátrixot A-val jelöljük, akkor elemeire könnyen hivatkozhatunk:

a(1,1)=2, a(1,2)=3, ..., a(3,2)=4, a(3,3)=3

A mátrixok szép matematikai struktúrákat alkotnak és nagyszerû példaprogramokat lehet rá írni,de ehhez szükség lenne arra, hogy indexelt adatstruktúrákat könnyebben kezeljünk. Ennek lehetôsége egy késôbbi fejezetben nyí lik meg számunkra, amikor is a JAVA tömb kezelését tanuljuk. A fenti

Page 32: Bevezetes a Java Programozasba

példa mátrix sorfolytonos felí rása alatt az

A=(2 3 1; 4 2 4; 1 4 3)

jelölést értjük.A 3 ismeretlenes egyenletek megoldásához a mátrixoknak egy fontos jellemzôjét,a determinánst, kell megértenünk. Egy n*n-es mátrix fôátlóját az a(1,1), a(2,2), a(3,3), ..., a(n,n) elemek alkotják, formálisan:

a(i,i) ahol i=1..n

A másik átlóban elhelyezkedô elemek a mellékátlót alkotják.

A determináns. Az A mátrix determinánsát detA-val jelöljük. 1. Egy 1*1-es mátrix determinánsa maga az egyetlen eleme. 2. Egy 2*2-es mátrixdeterminánsa alatt a fôátlóban illetve mellékátlóbanlévô elemek szorzatának küléönbségétértjük. Példa: Legyen

Ekkor detB=det(5 3; 4 2)=5*2-4*3=10-12=-2.

Tovább lépünk egyet. Egy n*n-es mátrix egyik eleméhez tartozó aldeterminánsa alatt azt az (n-1)*(n-1)-es determinánst értjük, mely azon mátrixnak a determinánsa, mely az adott elemhez tartozó sorba, illetve oszlopban szereplô elemek törlésével keletkezik. Egy 3*3-as mátrix determinánsát úgy képezzük, hogy kiválasztjuk az egyik sort/oszlopot és egy olyan elôjelben alternáló összeget képezünk, melynek tagjai a kiválasztott sor/oszlop elemei megszorozva az elemhez tartozó 2*2-es aldeterminánsokkal. Ez a fenti 3*3-as példa mátrixra nézve, kiválasztva például az elsô oszlopot az alábbiak szerint alakul:

detA=2*det(2 4; 4 3)-4*det(3 1; 4 3)+1*det(3 1; 2 4)=2*(-2)-4*5+1*10= -12

Vagyis az elsô elemhez tartozó aldetermináns az elsô sor és elsô oszlop törlésével keletkezô almátrix determinánsa, az a(2,1) elemhez a második sor és az elsô oszlop törlésével kapott 2*2-es mátrix- , s végül az a(3,1) elemhez pedig a 3. sor és elsô oszlop törlésével kapot 2*2-es mátrix determinánsa tartozik. Formálisan egy n*n-es A mátrix determinánsa a k. oszlop szerint kifejtve:

Page 33: Bevezetes a Java Programozasba

elemhez tartozó aldetermináns.

Egy A mátrix egy

oszlopvektorral való szorzatán azt az oszlopvektort értjük, melynek i. komponense az a(i,1)*x1+a(i,2)*x2+...+a(i,n)*xn összeg. Formálisan, ha A n*n-es mátrix és x n-dimenziós oszlopvektor, akkor

Ezzel el is érkeztünk ahhoz a ponthoz, ahonnan foglalkozhatunk az eredeti problémánkkal. A fenti módszerrel egy egyenletrendszer együtthatói egy mátrixot, egy ún. együttható-mátrixot alkotnak, mí g a kiszámítandó ismeretlenek egy oszlopvektort. Ezáltal az egyenletrendszer egyszerûen Ax= b alakban írható fel. Például a 3*3-as esetben:

Vegyük észre, hogy az ábrában szereplô A mátrix-ot soronként megszorozva az x oszlopvektor elemeivel, akkor éppen az egyenletrendszerünk egyenleteit kapjuk:

1) a11*x1+a12*x2+a13*x3=b1 2) a21*x1+a22*x2+a23*x3=b1 3) a31*x1+a32*x2+a33*x3=b1

A 3 vagy többismeretlenes egyenletrendszerek megoldására az egyenlô együtthatók módszerének általánosí tása a Gauss-féle elimináció, illetve a változó helyettesítés módszerének általánosí tásaként ismert Cramer-szabály kiválóan alkalmas. A Gauss-féle elimináció JAVA megvalósí tása a még nem tanult tömb adatszerekezet ismerete nélkül igencsak körülményes lenne, í gy most a Cramer-szabályt ismerjük meg.A dolog elég egyszerû, mindössze a fentiekben megtanult 3*3-as determinánsok számítását kell gyakorolnunk.

Page 34: Bevezetes a Java Programozasba

Amennyiben az A mátrix determinánsa nemzérus (detA!=0), akkor az x1, x2, x3 (...xn) ismeretlenek elôállnak a következô hányadosok képzésével:

D1/detA, D2/detA, D3/detA,

ahol D1, D2, D3, ... azon mátrixok determinánsai, melyeket úgy képezünk, hogy az A mátrix 1, 2, 3, ...oszlopait kicseréljük a jobb oldalon szereplô b együttható vektor elemeivel kicseréljük.

Például:

Amennyiben az együttható mátrix determinánsa nemzérus, akkor az egyenletrendszer határozatlan, ennek vizsgálatára azonban további matematikai ismeretek hiányában nem térünk ki. Nézzünk egy konkrét példát a Cramer-szabály alkalmazásával történõ megoldásra!

1) 4x1-3x2+ x3=2 2) x1+ x2-2x3=9 3) 2x1+ x2-3x3=14

azaz mátrixos alakban:

A determinánsokat az elsô oszlop szerint kifejtve:

detA=4*(-3+2)-(9-1)+2*(6-1)=-4-8+10=-2 detD1=2*(-3+2)-9(9-1)+14*(6-1)=-2-72+70=-4 detD2=4*(-27+28)-(-6-14)+2*(-4-9)=-2 detD3=4*(14-9)-(-42-2)+2*(-27-2)=6

Ily módon a Cramer-szabály szerint:x1=-4/-2=2 x2=-2/-2=1 x3=6/-2 =-3 Visszahelyettesítéssel ellenôrizve kész vagyunk.

Feladat

Page 35: Bevezetes a Java Programozasba

A fenti ismeretek értelmében készítsünk programot, mely megold egy 3*3-as, lineáris egyenletrendszert! A program tartalmazzon függvényt a 2*2-es, illetve 3*3-as determináns kiszámítására, valamint vizsgálja meg az alapmátrix determinánsának nemzérus voltát. A megoldást itt találod.

Ellenõrzõ kérdések 1. Mit nevezünk cimkének, hogyan cimkézhetünkutasí tásokat? 2. Ismertesd a többszörös elágazás készítésére alklamas utasítást! 3. Ismertesd a break utasítás használatát! 4. Mire használhatjuk a continue utasí tást? 5. Mikor nem használhatunk case és continue utasí tásokat? 6. Milyen módszereket ismersz 2*2-es, linaáris egyenletrendszer megoldására? 7. Mikor mondunk egy 2*2-es egyenletrendszert határozatlannak, illetve ellentmondásosnak? Hány megoldás létezik ezekben az esetekben? 8. Mit nevezünk mátrixnak? 9. Definiáld az alábbi fogalmakat! Egy mátrix - fôátlója, - mellékátlója, - determinánsa (2*2-es és 3*3-as esetben). 10. Mit értünk egy A n*n-es mátrix x n-dimenziós oszlopvektorral való szorzatán? 11. Hogyan hozható kapcsolatba az egyenletrendszerek megoldása és a mátrixok? 12. Ismertesd a Cramer-szabály-t 3 ismeretlenes, elsôfokú egyenletrendszerek esetén! Mi a megoldhatóság feltétele?

7. fejezet Fiókos szekrények garmadája, mindegyik hozzá való mamával. A Jáva alapépítõelemei, az objektumok. Objektumok deklarálása változókkal és függvényekkel, amelyeket ezek után metódusoknak fogunk hívni. Objektumok létrehozása és halála, életciklus a Jávában. A szemétgyûjtõ.

Emlékszel a mamára és a fiókos szekrényére a második fejezetben? Eddig határozottan egy készletnyi változóban és függvényben gondolkoztunk. Ez meg is felel a számítógépprogramozás hagyományos modelljének, ahol van egy darab fiókos szekrényünk, azaz adattárolónk és egy rakat függvényünk, ami ezen az egy állapottárolón manipulál. Ez nagyon kellemes is mindaddig, mí g a programod kellõen nagyra nem nõ, utána azonban ekkor a program különbözõ funkciói és azoknak különbözõ változói igencsak összekavarják a dolgokat. Képzelj el most egy olyan modellt, ahol a program különbözõ funkcióit a hozzájuk tartozó változókkal egy egységbe fogjuk. Ez még nem elég: ilyen egységeket szabadon hozhatunk létre, egyfajtából akár többet is, mindegyiket saját változókészlettel, hí vhatjuk

Page 36: Bevezetes a Java Programozasba

függvényeiket, amelyek a saját változókészleten manipulálnak és aztán dönthetünk az egység elpusztí tásáról is, ha nincsen már rá szükségünk. Olyan ez, mintha a programunkat úgy építhetnénk, mint valami gépet; elõször megtervezzük az alkatrészeit, majd legyártjuk belõlük a megfelelõ mennyiséget - csavarból százával, fõdarabból csak egyet-kettõt. Ezek után megmondjuk, az alkatrészek hogyan legyenek összekötve, majd beindítjuk a gépet, mire az mûködni kezd. A fent leírt folyamat vészesen hasonlít az objektumorientált programok készítésére, mint amilyeneket Jávában is írunk.

Az objektumorientált programozásban az alkatrészeket objektumnak hí vjuk. Objektumokat tervrajz alapján készítünk, pont annyit, amennyi kell. A tervrajzot, amibõl egy bizonyos fajta objektumokat legyártunk, az adott objektumhoz tartozó osztálynak hí vjuk. Az objektumok összeköttetéseit az objektum függvényeinek, objektumorientált terminológiával metódusainak hí vásával valósítjuk meg. Valahányszor létrehozunk egy objektumot, lefoglalódik hozzá az összes, az osztállyal deklarált változó (tehát nem azok, amelyeket a metódusokban deklaráltunk), ezeket a változókat egyedváltozónak hí vjuk, merthogy minden objektumegyednek van belõlük egy teljes készlet. Minden objektum tehát az egyedváltozóin keresztül önálló egyéniség lehet.

Hagyjuk most a hosszas fejtegetést és nézzünk egy példát! Ismerõs?

public class Elso { public void prog( String args[] ) { System.out.println( "Hello, ez az elso Java programunk!" ); } public static void main( String args[] ) { Elso e = new Elso(); e.prog( args ); } }

Van itt egy rettenetes titok, ami az egész tananyag eleje óta lappang itt a kék ködök mögött: igazából mindig is objektumorientált programokat írtunk, mert Jávában máshogy nem lehet. Minden programunk egy osztálydefiníció volt és mindegyikben saját magunk is létrehoztunk legalább egy objektumot. Nézzük most át ezt a programot ebbõl a szempontból!

Mindenekelõtt definiáltuk az Elso nevû osztályt. Ezt a következõképpen tettük:

class Elso { ... }

ahol a kapcsos zárójelek között az osztályhoz tartozó cuccok vannak, ebben az esetben két metódus. Egészen pontosan nem ezt tettük, hanem még elé írtuk azt, hogy public. Egyelõre errõl elég annyit tudnunk, hogy a programunk futtathatósága miatt szükséges, hogy a program fõosztálya (amit a java parancs után írunk) public legyen. Ha egy forrásfájlban van egy public osztály, annak neve meg kell egyezzen a forrásfájl nevével. Ezen felül a fájlban tetszõleges számú egyéb class lehet public nélkül. Egyelõre jegyezzük meg azt, hogy a nem public osztályt csak az õt tartalmazó forrásfájlból lehet elérni. Ez nem teljesen pontos í gy, de egyelõre elég nekünk. Úgyse írunk még olyan programot, aminek forrása több fáj lban van.

Nézzük meg most ezt a sort:

Elso e = new Elso();

Page 37: Bevezetes a Java Programozasba

Ez egy értékadás, de furcsa fajtájú, nem? Mindenekelõtt mi az Elso típus, mi csak int-et, double-t és boolean-t ismerünk. Az Elso egy új típus, egy objektumreferencia. A referencia típusú változó is csak egy fiók, de ebben egy adott osztályú objektumra hivatkozó referenciát, mutatót tárolunk. Egy referenciát úgy lehet elképzelni, mint egy elérési instrukciót: vedd balról harmadik bögrét a második polcon. Maga a bögre felel meg az objektumnak. Ha most kicseréljük a fiókban levõ cetlit egy másikra, ami a harmadik polcon a legszélsõt jelöli meg, akkor az, aki kiveszi a cetlit a fiókból, ezt a bögrét fogja megtalálni és nem az elõzõt. Tányérra mutató cetlit nem lehet a fiókba elhelyezni, ez a fiók a bögrereferenciáké.

Az e változó tehát egy Elso objektumra mutató referenciát tárol, de hogy jutunk ilyenhez? A new operátor használható arra, hogy egy már definiált osztályból egyedeket gyártson. Most egy Elso osztályú objektumból hoztunk létre egy új egyedet, erre az egyedre mutató referenciát szereztünk a new-tól és ezt rögtön hozzá is rendeltük az e változóhoz. Az e-n keresztül tehát most ezt a példányt tudjuk elérni.

Az objektumreferencia típusnak egyetlen definiált konstans értéke van, egy speciális referencia, ami nem mutat sehova. Ennek a konstansnak a neve null. Ha egy objektumreferenciának null értéket adunk, ezzel azt jeleztük, hogy a referencia "nem használható", mert nem áll mögötte objektum. Példa:

e = null;

A null egy különleges érték mert nincs tí pusa, bármilyen típusú objektumreferenciának értékül adható.

Mit tudunk tenni egy referenciaváltozóval, milyen mûveletek állnak rendelkezésre ilyen típusú értékekkel? Mindenekelõtt más megegyezõ típusú változónak lehet értékül adni. Ha azt mondanánk

Elso m; m = e;

akkor az m és az e ugyanarra az objektumra hivatkozik, mindkettõn keresztül ugyanazt az objektumpéldányt érhetjük el. Ha ezek után tovább variálunk:

e = new Elso();

akkor létrehoztunk egy új egyedet az Elso-bõl és most az e erre mutat. A két változón keresztül most két különbözõ objektumpéldányt érhetünk el. De mit jelent az, hogy elérünk egy objektumot egy referencián keresztül?

e.prog( args );

A referenciát most felhasználtuk arra, hogy meghí vjuk az általa hivatkozott objektum egy metódusát. A függvényhí vás pont úgy néz ki, mint egy eddigi mezei függvényhí vás, de most a referencia megadásával jeleztük, hogy nem a saját objektumunknak, hanem egy másiknak, a referencia által mutatottnak a metódusát akarjuk.

Egyelõre nem mondom el, miért van szükség arra, hogy programunk saját osztályát példányosítsuk, az majd egy kicsit késõbb jön. A static a kulcszó.

Minden objektumnak van egy speciális metódusa, amit konstruktornak hivunk. A konstruktor arról ismerkszik meg, hogy nincsen visszatérési értéke (void sem!) és a metódusneve megegyezik az osztály nevével. Paraméterei akárhányan lehetnek és persze egy objektumnak lehet több konstruktora

Page 38: Bevezetes a Java Programozasba

is különbözõ fajtájú és típusú paraméterekkel. Nézzünk egy példát! Ha az Elso objektumnak lenne konstruktora, azt így kellene leírni.

Elso( int i ) { System.out.println( "Konstruktor lefutott, parameter: "+i ); }

Ez speciel egy olyan konstruktor, ami egy darab egészet vár paraméterként és akkor hí vódik meg, ha pont egy egészt adtunk a meghí vásakor. De hogyan és mikor hí vódik meg a konstruktor? A konstruktort az egyed létrehozásakor hí vja meg a rendszer (tehát nem mi) és a paramétereit a new mögött álló osztálynév mögött levõ gömbölyû zárójelek közül szerzi. Ha azt akarjuk, hogy ez a konstruktor hívódjon meg, az Elso egyedet í gy kell létrehozni:

Elso e = new Elso( 2 );

Mint mondtam, minden objektumnak van konstruktora. De akkor hol az Elso konstruktora, nincs olyasmi a kódban, hogy

Elso() { ... }

pedig mi eddig ví gan hivatkoztunk rá, amikor azt mondtuk

Elso e = new Elso();

A megoldás az, hogy a rendszer van olyan kedves és egy konstruktort, a paraméter nélkülit automatikusan létrehozza nekünk, ha mi nem deklaráljuk. Í gy aztán még ha nem is foglalkozunk olyan földi hí vságokkal, mint a konstruktor, objektumunkat biztosan létrehozhatjuk a paraméter nélküli konstruktoron keresztül. A paraméter nélküli konstruktort alapértelmezett konstruktornak (angolul default constructor) hí vjuk.

Feladat

Módosítsd népszerû Elso programunkat úgy, hogy legyen az Elso osztálynak egy paraméterrel rendelkezõ konstruktora is továbbá az is hí vódjon meg! Ha valamiért nem megy, itt a megoldás , de a lecke szövege tartalmazza az összes szükséges programdarabot, úgyhogy ezt meg kell tudni oldanod! Futtasd le a megoldást és értelmezd az eredményt! Ezek után deklarálj egy paraméterek nélküli konstruktort is és alakítsd át a programot úgy, hogy az hí vódjon meg! A paraméter nélküli konstruktorba is tegyél egy kií rást, hogy biztosak legyünk, tényleg az hí vódott-e meg. Futtasd le, értelmezd az eredményt majd ellenõrizd a megoldást!

A konstruktorokat persze elsõsorban nem arra használják, hogy buta kií rásokat eszközöljenek velük, hanem a születõ objektumpéldány belsõ állapotának beállí tására, vagyis fõképp arra, hogy az objektum egyedváltozói (emlékszel, ezek azok a változók, amelyeket nem a metódusok törzsében, hanem az osztály törzsében, a metódusokkal egy szinten deklarálunk) megfelelõ kezdõértéket kapjanak. Í gy aztán egy osztály törzse í gy nézhet ki:

class ValamiOsztaly { int parameter; ValamiOsztaly( int parameter_init ) { parameter = parameter_init; }

Page 39: Bevezetes a Java Programozasba

void irdkiaParametert() { System.out.println( "Parameter: "+parameter ); } }

Aztán valahol egy felhasználó kódrészletben mondhatunk valami ilyesmit:

ValamiOsztaly v = new ValamiOsztaly( 12 ); ... v.irdkiaParametert();

és ez persze 12-t írna ki. Az objektum létrehozasakor a konstruktor meghívodik a new utan levõ értékkel. Ezt a konstruktor berakja az objektumegyed parameter nevû változójába. Amikor késõbb meghí vjuk ugyanazon objektumegyed egy metódusát, ami a parameter változót felhasználja és mellékesen ki is írja. Egy kicsit olyan, mint az itt-a-piros-hol-a-piros, te tudtad követni?

Ha már az egyedváltozóknál tartunk, az objektumreferenciát felhasználhatjuk arra, hogy egy egyedváltozót direktben elérjünk. Elõzõ példánkban például azt is mondhatjuk:

int k = v.parameter;

ahol is egy frissen létrehozott k nevû változóba pakoltuk a v referenciával hivatkozott objektum parameter nevû változójának értékét.

Megjegyzendõ, hogy az objektum egyedváltozóinak direkt elérése nem javasolt módszer, az obejktum belsõ állapotát a metódusain keresztül illik módosítani és lekérdezni. Ez azonban nem kötelezõ, csak afféle ajánlott programtervezési módszertan.

Feladat

Ragadd meg nagy sikerû Elso programunkat és alakítsd át Masodik-ká! Az új változatban próbáld ki a következõ kunsztokat:

I. Legyen az osztálynak egy ertek nevû egész típusú egyedváltozója

II. Az alapértelmezett konstruktor adja ennek a 0 értéket

III. Legyen egy olyan konstruktor, ami egy egész paramétert vár és az ertek változóba írja

IV. Legyen az osztálynak egy olyan metódusa, ami kiírja az ertek-ben levõ értéket

V. A programodban hozzál létre két objektumegyedet, az egyiket az alapértelmezett konstruktorral, a másikat az egész paraméterrel rendelkezõ konstruktorral.

VI. Hí vd meg mind a kettõ objektumegyednek az ertek-et kiíró metódusát

VII. Érd el az ertek változót direktben a fõprogramodból és írd ki úgy is!

Mindenképpen próbáld a programot megírni magad és ha készen van, nézd meg a megoldást!

Page 40: Bevezetes a Java Programozasba

Most már tudunk objektumokat létrehozni, de hogyan tûnnek ezek el? Minden objektumhoz tartozik memóriaterület, amelyet a rendszer lefoglal számára, amikor az objektum létrejön. Ha egyre csak foglalgatunk, felesszük elõbb-utóbb a gép memóriáját és a programunkra szomorú vég vár.

A Jáva sajátos módon oldja meg a már nem használt objektumok felszabadítását. A háttérben egy szemétgyûjtõ programot futtat, ami megtalálja a már nem használt objektumpéldányokat és felszabadítja õket. Mi alapján dönti el, ki a szükségtelen és ki nem? Minden objektum szükségtelen, amire már nincsen referencia. Például kiváló Masodik programunkban ha azt írnánk:

m1 = new Masodik( 3 ); m1 = new Masodik( 4 );

akkor a 3-as paraméterrel létrehozott Masodik egyedre a második értékadás után már nem mutatna referencia. Ugyanez a trükk akkor is igaz, ha a referenciaváltozó automatikusan takarítódik el, például egy metódus belsõ változójának megszûnése miatt.

void objektumLetrehozo() { Masodik m1 = new Masodik(); }

Ebben az esetben az m1 változó megszûnik, amikor a metódus visszatér a hí vóhoz és minthogy a referenciát nem mentettük el egy biztos helyre (pl. objektum egyedváltozóba vagy a metódus visszatérési értékébe, ahol a hívó esetleg felhasználhatja), a létrehozott Masodik egyed szemétté válik.

A szemétgyûjtõ nem azonnal takarítja el a feleslegessé vált objektumokat, hiszen akkor programunkat alaposan lelassítaná állandó sepregetésével. Hasonlatosan egy rutinos házmesterhez, elõbb megvárja, hogy elég sok szemét legyen és akkor lendül akcióba. Mindez megmagyarázza, miért olyan hihetetlenül magas a Jáva programok memóriaigénye. Egy jól irányzott Jáva programsorral akár 3-4 szemétobjektumot is tudunk csinálni és egy komoly Jáva program terhelés alatt akár 2-3KBájt szemetet is létrehoz másodpercenként. Ezért cserébe viszont nem kell törõdni az egyik legundokabb hibával, a memóriafolyással, amikor a programozó elfelejti felszabadítani a lefoglalt és már nem használt memóriaterületet és a gép szabad memóriája lassan elfogy.

A szemétgyûjtõ mûködésének demonstrálására nem tudtam látványos és egyszerû példát kitalálni, í gy sajnos el kell hinned, hogy ez í gy van. Lássuk inkább a lecke fináléját, í me egy nagyszerû

Feladat

Í rjál egy programot, ami egy játékost és egy bankot modellez, akik fej vagy í rást játszanak forintos alapon. A ját ékos fogad fejre vagy í rásra, a bank feldobja a pénzt és ha a játékos eltalálja, kap egy forintot a banktól, ha nem, õ fizet egyet. A játékos fogadjon véletlenszerûen, játsszanak ilyen mérkõzésbõl 10-et majd a végén írd ki, kinek mennyi pénze maradt. A feladat megoldásához mindenekelõtt meg kell tervezned a programban használt objektumokat. Minden szereplõt azonosítson egy objektum! Ezek szerint biztosan van Játékos és Bank objektumunk. Í rd fel a két objektum tulajdonságait a következõ formában:

xxx objektumnak van - ... (az objektum által tárolt adatok listája) xxx objektum képes

Page 41: Bevezetes a Java Programozasba

- ... (az objektumon végrehajtható mûveletek listája) xxx objektum kommunikál - ... milyen objektumokkal kommunikál az objektum (csak ha mi vagyunk a kezdeményezõ)

Alkalmazd a fenti módszert a feladatunkra! A megfejtéshez görgess le néhány sort, de ennek mennie kell.

A Játékosnak van - pénze A Játékos képes - fogadásokat tenni - kiírni, mennyi pénze van A Játékos kommunikál - A Bankkal, amikor fogadásokat tesz

A Banknak van - pénze A Bank képes - Fogadásokat fogadni, dobni, eldönteni, hogy a játékos vesztett vagy nyert - kiírni, mennyi pénze van A Bank kommunikál - Senkivel, a Játékos kezdeményezi a fogadásokat

Ezen felül van még egy szereplõ, a Fogadó, ami a szálakat mozgatja. Õ veszi rá a Játékost, hogy 10-szer fogadjon a Banknál. A Fogadónak van - Játékos objektuma - Bank objektuma A Fogadó képes - lejátszatni a mérkõzést A Fogadó kommunikál - A Játékossal, hogy fogadjon

A "van" szekcióból derülnek ki az objektum egyedváltozói. A példában a Játékosnak és a Banknak van pénz változója, a Fogadó meg referenciát tárol a Játékos és Bank objektumokra. A "képes" szekcióból derül ki, milyen metódusai lesznek. A Játékosnak lesz egy metódusa, amivel fogad és egy másik, ami kiírja a Játékos objektumban tárolt pénzt. A Banknak lesz egy metódusa, aminél fogadásokat lehet tenni és egy másik, ami kiírja a bank pénzét. A Fogadónak egy metódusa lesz, ami a játszmát lejátsza. A "kommunikál" szekcióból derül ki, melyik objektum melyik másik metódusait hí vja meg, kire tárol referenciákat. A Játékos referenciát tárol a Bankra és meghí vja annak fogadáskiértékelõ metódusát, a Fogadó pedig a Játékosra tárol referenciát. A továbbiak kedvéért a Bank objektumot is a Fogadó hozza létre és passzolja a Játékosnak a refernciáját a Játékos konstruktorán keresztül. Az alábbi ábrán ezeket a relációkat ábrázoltam.

Page 42: Bevezetes a Java Programozasba

A fentiekben leírt egyszerû módszert objektumelemzésnek fogom hívni ezután. A módszer valóban egy profi programozók által is használt programtervezési módszertan lényegesen leegyszerûsített változata, amely most oktatásra teljesen megfelel.

A sok duma után írd meg a programot! Nézd meg az ábra függõségi listáját és ebbõl kiderül, célszerû a Bankkal kezdeni, mert az nem függ semmitõl, utána jöhet a Játékos, majd a Fogadó. Ha készen van, csinálj egy olyan változatot, ahol három játékos játszik ugyanannál a banknál. Ha nem untad meg, csinálj olyat is, ahol három játékos játszik három különbözõ banknál! Pusztán csak ellenõrzésképpen itt a megoldás.

Feladat

Í rjál egy olyan programot, ami három játékost modellez, akik a kõ-papír-ollót játszanak körmérkõzéses alapon. Ha emlékszel, a kõ-papír-olló az a klasszikus játék, ahol a két játékos egyszerre választ a három tárgy közül és egy körkörös szabályrendszer alapján döntik el, ki gyõzött: a kõ gyõz az olló ellen, de veszít a papír ellen, a papír pedig veszít az olló ellen. Futtasd le a körmérkõzést 20-szor és irasd ki, melyik játékos hányszor nyert. Minden játékosnak legyen egy száma, ami azonosítja.

Elõször végezd el az objektumelemzést, ezt itt ellenõrizheted. Ezután készítsd el a programot, ha készen vagy, ellenõrizd a megoldást! Figyeld meg, hogy a megoldásunkban most nem tároltuk el a játékostárs refernciáját a saját objektumunkba (pedig megtehettük volna, hiszen a párok változatlanok), hanem a rugalmasabb szerkezet érdekében paraméterként adjuk át a fogadó metódusnak. Ha netán eltároltad volna apartner referenciáját, az is helyes.

Ellenõrzõ kérdések

I. Mi az objektum, mi az osztály és mi a kapcsolatuk?

II. Mi az összefüggés a függvények és metódusok között?

III. Mi az egyedváltozó?

IV. Mi az objektumreferencia

V. Mit jelent a null?

VI. Hogyan hí vhatjuk meg egy objektum metódusait az objektumreferencián keresztül?

VII. Mi a konstruktor?

VIII. Hogyan hí vjuk meg a konstruktort?

IX. Mi az alapértelmezett konstruktor?

Page 43: Bevezetes a Java Programozasba

X. Mikor generál alapértelmezett konstruktort a fordító maga?

XI. Hogyan hivatkozhatunk egy objektum egyedváltozóira az objektumreferencián keresztül?

XII. Mit jelent az, hogy a Jáva rendszerben egy szemétgyûjtõ mûködik?

8. fejezet Újabb megvilágosodás: statikus változók és metódusok. Egyedváltozók és osztályváltozók kavalkádja. Öröklõdés és konverziók.

Mint említettem, egy normális, mezei osztálynak egyedváltozói vannak, amelyek minden egyedben külön létrejönnek és külön életet élnek az egyes egyedekben. Miután létrejöttek, semmi közük egymáshoz. Ugyanúgy, a mezei osztály metódusai az egyedváltozókon manipulálnak, í gy amikor meghí vunk egy metódust pl.

j1.eredmenyKiiras();

akkor implicit módon odaadtuk a j1 által hivatkozott objektumegyedet is az eredménykiíras metódusnak, ami aztán buzgón felhasználja ezen objektumegyed változókészletét. Ez eddig rendben is van. Lehetnek azonban esetek, amikor nem akarjuk, hogy a változó minden egyedben létrejöjjön, hanem csak egyet akarnánk, közöset minden objektumegyednek, ami az adott osztályból példányosítódott. Ha netán ilyenben törnénk a fejünket, a statikus változók pont nekünk valók. A statikus változó pont olyan, mint bármely változó, csak a típusdeklarációja elé még oda van írva az hogy static. Í gy:

static int szamlalo;

Az ilyen változó nem jön létre újra meg újra, valahányszor az osztályt példányosítjuk, csak egyszer, amikor a Jáva virtuális gép elõször tölti be az osztálydefiníciót. A konstruktorral összeházasítva például felhasználhatjuk ezt a fajta változót arra, hogy megszámolja, hányszor példányosították az adott osztályt. A következõ példában ezt tesszük.

class Szamlalo { static int peldanyok = 0; Szamlalo() { ++peldanyok; } }

Ezek után valahányszor azt mondjuk: new Szamlalo(), a peldanyok valtozo megnõ eggyel.

Statikus változóra kétféleképpen hivatkozhatunk: vagy egy referencián keresztül, mint más becsületes változóra vagy egyszerûen az osztály nevével pl. í gy:

int k = Szamlalo.peldanyok;

Page 44: Bevezetes a Java Programozasba

Ez utóbbi azért lehetséges, mert a peldanyok változó nem az objektumegyedeknek, hanem az osztálynak lefoglalt memóriaterületen tárolódik, eléréséhez nincs szükség objektumreferenciára. A statikus változóknak ezt a tulajdonságát elõszeretettel használják fel arra, hogy konstansokat deklaráljanak vele. Ha pl. a Szamlalo osztályban azt mondom:

static int Milennium = 2000;

azt késõbb bárhonnan kényelmesen elérhetem Szamlalo.Milennium formában anélkül, hogy mindenféle referenciákra kellene vadászni. A konvenció szerint (amit a Jáva nem tesz kötelezõvé, csak követni ajánlják) a konstansok nevét csupa nagybetûvel kell írni, hogy megkülönböztessük a változó változóktól. Í gy e:

static int MILENNIUM = 2000;

Feladat

Vedd elõ nagy sikerû kõ-papír-olló programunkat és valósíts meg benne két szolgáltatást a statikus változók segítségével! Elõször is számolja meg és a futás végén írja ki, hány döntetlen eredmény született összesen. A Jatekos osztályban hozzál létre egy statikus változót és növelgesd, valahányszor döntetlen fordult elõ. Másodszor is a kõ, papír, olló tárgyakhoz rendelt számokat helyettesítsd konstansokkal. Í me az én változatom!

Metódusok is lehetnek statikusok. Ha egy metódusnév elõtt áll a static kulcsszó, ennek három következménye lesz.

I. A metódus csak statikus változókat érhet el. Ne feledd: a statikus metódus hí vásakor nem ismeri az objektumegyed kontextusát, í gy aztán az egyedváltozókat nem tudja elérni.

II. A metódus hí vható osztálynév.metódusnév formában. Példa: Math.random() (ugye ismerõs?)

III. A metódus nem hí vhat nem statikus metódusokat. Ez ugyanazon ok miatt van, amiért egyedváltozókat sem érhet el.

Statikus metódusoknak a legfõbb haszna az, hogy nem kell érte példányosítani az osztályt, meghí vható egyszerûen osztálynév alapján. Ezzel kitörhetünk a Jáva erõszakos objektumszemléletébõl és mindenhonnan elérhetõ függvényeket írhatunk.

Feladat

Fogd az Elso.java programot és érd el, hogy ne kelljen az Elso-t példányosítani! Töröld ki tehát a következõ sort

Elso e = new Elso();

és tedd meg a szükséges változtatásokat, utána ellenõrizd a megoldást!

Most már talán megértheted, miért hagytuk ilyen sokáig burjánzani a misztikumot a main-ban található két sor körül. Minthogy a main statikus (í gy szól a Jáva specifikáció), mert a Jáva virtuális gép nem példányosítja a java parancs által hivatkozott osztályt, í gy nem érhet el nem statikus változót vagy

Page 45: Bevezetes a Java Programozasba

metódust. A sok static megzavart volna és nem is tudtuk volna ilyen könnyedén bevezetni az egyedváltozókat. Mostantól azonban tudjuk, mit csinálunk, í gy a programosztályt csak akkor példányosítjuk, ha kell.

És most valami teljesen más! Megmutatom, hogyan lehet minimális munkával okos objektumokat készíteni. Eddig azt tanultuk, hogy az osztályok változóit és metódusait nekünk kell deklarálni. Ha azonban olyan lusta vagy, mint amilyen én, inkább veszünk valami készet és átalakítjuk csupán azt módosítva benne, amiben a kész nem tesz eleget igényeinknek. A módszert, ami objektumorientált rendszerekben erre a célra rendelkezésre áll, öröklõdésnek hí vjuk.

Öröklõdéssel azt jelenthetjük ki, hogy egy osztályt nem a nulláról hozunk létre, hanem átveszünk egy létezõ osztályból mindent, majd bõvítjük illetve fölüldefiniáljuk az új funkciókkal. Vegyük a következõ példát:

class Eredeti { int i; void novel() { ++i; } } class Leszarmazott extends Eredeti { void novel() { i = i + 2; } void csokkent() { i = i -2; } }

A kicsit buta példánkban (miért kell ahhoz leszármazás, hogy megspóroljunk egy int i;-t?) a leszármazott osztályban felülírtuk a novel() metódust, hogy ezentúl kettõvel növelgesse i-t és hozzáadtunk egy csokkent metódust, ami csökkentgetni is tud. A Leszarmazott osztályt pont úgy kell használni, mint az Eredeti-t.

Még egy megjegyzés: a konstruktorok nem öröklõdnek. Ha a leszármazott osztálynak is szükségük van spéci (tehát paramétert fogadó) konstruktorokra, azt újra kell definiálni a leszármazott osztályban is. Ha nincs szükség változtatásra, használhatod a super() hí vást, amivel a szülõosztály konstruktora felé passzolhatod a paramétereket. Vegyünk egy példát, ahol egy egész paramétert passzolunk felfelé:

class Leszarmazott extends Eredeti { Leszarmazott( int i ) { super( i ); } ... }

Az öröklõdés kiválóan alkalmas arra, hogy bevezessek egy régen esedékes fogalmat, a típuskonverziót. Típuskonverzióval hasonló típusú értékeket alakíthatunk egymásba. Kézenfekvõ

Page 46: Bevezetes a Java Programozasba

példa a számok: miért kell fallal elválasztani egymástól az int-et és double-t? Nem kell és í me egy kis példa, hogyan lehet típuskonverzióval egymásba alakítani õket.

double n = 2.7; int ni = (int)n; n = (double)ni;

Az ni változóba átkonvertáltuk n-t. Csoda azonban most se történt, ni-be 2 került, a törtrész levágódott. A következõ lépésben az n-be visszakonvertáltuk az ni értéket, minek eredményeképpen n-ben most 2.0 van.

Ez eddig nagyon egyszerû, de mik lehetnek hasonló típusok még? A Jáva nagyon szigorú típuskonverzió terén és a számok mellett csak a leszármazott osztályok referenciáit engedi egymásba átkonvertálni. Például az elõbbi példánknál maradva ha netán azt mondanánk

Leszarmazott l = new Leszarmazott(); Eredeti e = (Eredeti)e;

Ez sikerülne, mert az Eredeti és Leszarmazott "hasonló" típusok. Azért megengedett a mûvelet, mert az Eredeti legalább azt tudja, amit a Leszarmazott, mert belõle jött létre módosí tással. Ha ugyanezt visszafelé csinálnánk, kicsit bonyolultabb a helyzet.

Leszarmazott l2 = (Leszarmazott)e;

Ezt a fordító elfogadja, de azért még nem biztos, hogy helyes. Ha a három utasí tás úgy követte egymást, ahogy az elõbb leírtuk, akkor helyes, hiszen e-ben valójában egy Leszarmazott referenciája van. Ha azonban netán ez lenne a sorrend:

Eredeti e = new Eredeti(); Leszarmazott l2 = (Leszarmazott)e;

ezt a fordító ugyan nem tekinti hibának (rokon osztályok között a konverzió megengedett), de a Jáva virtuális gép a futás során észreveszi a turpisságot és hibaüzenettel leáll.

Most már csak az a kérdés, ha vesszük az elõzõ példát

Leszarmazott l = new Leszarmazott(); Eredeti e = (Eredeti)e;

és meghí vjuk a novel metódust

e.novel();

vajon eggyel vagy kettõvel nõ-e az i változó? A Jáva dinamikus típusfeloldással rendelkezik, tehát mindig annak az osztálynak a metódusai hí vódnak meg, ami az osztály valójában, nem pedig a referencia típusa szerint, i tehát kettõvel nõ. Referenciáinkat tehát nyugodtan konvertálhatjuk a szülõosztály felé, ha elhasználjuk a referenciát, az a megfelelõ eredménnyel fog járni.

Feladat

Ragadd meg barátunkat, a kõ-papír-olló programot és csináld meg azt, hogy egy játékos azzal a "szisztémával" játszik, hogy mindig pl. kõre fogad. Csinálj egy leszármazottat a Jatekos osztályból és írd felül a fogadas() metódust megfelelõképpen! Ezek után az egyik játékost a leszármazott osztály

Page 47: Bevezetes a Java Programozasba

szerint hozdd létre és használj típuskonverziót a típushibák elkerülésére! Itt a megoldás ellenõrzésképpen. Ki a buta játékos?

Ellenõrzõ kérdések

I. Mi a statikus változó?

II. Elérhetõ-e a statikus változó nem statikus metódusból?

III. Milyen referenciával lehet elérni a statikus változót?

IV. Mi a statikus metódus?

V. Elérhet-e normál egyedváltozót statikus metódus?

VI. Meghí vhat-e statikus metódust normál, nem statikus metódus?

VII. Meghí vhat-e normál metódust statikus metódus?

VIII. Mit jelent az, hogy egyik osztály leszármazottja a másiknak?

IX. Lehet-e egy osztályreferenciát a szülõosztály felé konvertálni?

X. Lehet-e egy osztályreferenciát a leszármazott osztály felé konvertálni?

XI. Lehet-e Jávában különbözõ típusú értékek között értékadás?

XII. Ha létrehozunk egy egyedet és egy szülõosztály típusa szerinti referenciával hivatkozunk rá, a szülõosztály vagy a leszármazott osztály szerinti metódus hí vódik-e meg?

9. fejezet Adatok tömegesen: tömbök a Jávában. Tömbtípusok. A Jáva tömbök is csak objektumok. Objektumok és tömbök tömbje.

Emlékszel lottós példánkra az 5. fejezetben, ott öt változóban tároltuk a lottószámokat. Talán már akkor megfordult a fejedben, hogy esetleg van egy elegánsabb módszer egymáshoz tartozó azonos típusú adatok tárolására. Ha a fiókosszekrénynél maradunk, mód van valami olyasmire, mint egy katalógusszekrény, ahol bizonyos kulcs (általában kezdõbetû) alapján érhetjük el az elemeket. A katalógusszekrénynek megfelelõ eleme a Jávának is van, de mi egy egyszerûbb adattípussal kezdjük, ahol az elemeket egy számmal, az indexszel lehet kiválasztani. Ezt az adattípust szinte minden számítógépes nyelv ismeri, tömbnek hí vják.

Legegyszerûbb egy példával kezdeni. Nézzük a következõ sort:

int a[] = new int[20];

Page 48: Bevezetes a Java Programozasba

Különösebb magyarázat nélkül (majd jön az is!) egyelõre fogadjuk el, hogy ez a sor egy 20-elemû tömböt kreál, amelyben minden tömbelem egy int. Az tömbelemeket egy egész típusú indexszel választjuk ki: a tömb elsõ eleme a[0], a második az a[1], az utolsó az a[19] (minthogy 0-ról kezdtük a számlálást!) A tömbelemeket ugyanúgy kezelhetjük, mintha normális változók lennének, értéket adhatunk nekik:

a[0] = 42;

vagy pedig felhasználhatjuk az értékeit kifejezésekben:

a[2] = a[0] + a[1];

Természetesen az indexnek nem kell konstansnak lennie, tetszõleges egész kifejezés megteszi. Példának okáért a következõ programrészlettel tölthetjük fel az egész tömböt 42-vel.

for( int i = 0 ; i < 20 ; ++i ) a[i] = 42;

Ilyen egyszerû ez a felszí nen! Most vizsgáljuk meg a tömböt létrehozó sort, mert érdekességek találhatók ottan. Az objektumoknál megszokott módon nézzük a tömbváltozót magát:

int a[];

Ez a változó egy int-ekbõl álló tömbre mutató referencia. Ugyanúgy, mint egy objektumreferencia (mint ahogy valójában objektumreferencia is) egy mutatón kí vül nincs lefoglalt tár terület mögötte. A 20 egész értékünk nem itt van. A referencia akkor lesz használható, ha létrehozunk egy tömbobjektumot, amire a referencia mutathat. Ilyen objektumot gyárt az értékadás jobb oldalán álló kifejezés.

new int[20];

Ez a kifejezés 20 egész számot befogadni képes tömbnek foglal helyet és visszatérési értéke a tömbre mutató referencia. Ezt tesszük bele az "a" tömbreferenciára, minek eredményeképpen az "a" változón keresztül indexelni tudjuk a tömböt. Az "a" változó - hasonlóan bármilyen más objektumrefernciához - tehát attól kap értelmet, amire mutat. Í rhatjuk késõbb, hogy

a = new int[40];

Ekkor a régi tömb, amire "a" imént mutatott eltûnik (vagy elérhetõ egy másik referencián keresztül, ha volt ilyen) és "a" egy vadi új, ezúttal 40 elemû tömböt tud elérni.

A tömbök tehát a Jávában teljesen úgy viselkednek, mint az objektumok. Ha van egy tömb típusú referenciánk, azzal "rámutathatunk" egy már létezõ tömbre, í gy két tömbreferencián keresztül érhetjük el ugyanazt a tömböt. Példa:

int a[] = new int[20]; int b[]; b = a; a[0] = 1; b[1] = 13;

A két értékadás a programrész végén ugyanannak a tömbnek a 0. és az 1. elemét manipulálja, minthogy a 3. sorban megtettük, hogy a "b" is ugyanarra a tömbre hivatkozzon.

Page 49: Bevezetes a Java Programozasba

A tömbök annyira objektumszerûek, hogy saját egyedváltozóik is vannak. Minden tömbobjektumnak van egy length egyedváltozója, ami megmondja a tömb méretét. Elõzõ példánkban

a.length

és

b.length

egyaránt 20-at ad vissza értékként (merthogy igazából ez ugyanaz a tömb).

A Jáva teljes típus- és indexhatárellenõrzéssel rendelkezik. Nem tehetjük meg azt, mint a C-ben, hogy a tömbindexet a tömb méreténél nagyobbra állítjuk és telepiszkítjuk a memóriát. Amikor egy tömbreferencián keresztül elérünk egy tömbobjektumot, a Jáva ellenõrzi a tömbobjektum tényleges méretét (dinamikusan foglaltuk, fordí tásidõben nem jöhet rá) és kilövi a programot, ha túlindexelni próbálja a tömböt.

Feladat

Í rjuk át a már elkészített lottó programot úgy, hogy a véletlen számokat egy 5 elemû int típusú tömbbe helyezze el! ! Gyakorlásképpen használjunk statikus metódusokat és adattagokat! A megoldást itt találhatod.

Feladat

I. Készítsünk programot, mely feltölt egy 100 elemû, véletlen számokból álló tömböt, majd az elemeket kilistázza a konzolra! Gyakorlásképpen használjunk statikus metódusokat és adattagokat! Az osztály neve legyen "Osszegzes"!

II. Bõvítsük a programot úgy, hogy adjuk össze az így generált tömb elemeit, majd az összeget írjuk ki a konzolra!

III. Válogassok le az eredeti tömbbõl a páros, illetve páratlan számokat egy "paros" illetve "paratlan" nevu tömbbe, listázzuk ki külön a páros, illetve páratlan számokat, illetve írjuk ki ezek arányát! A megoldás az Osszegzes.java programban található, az 2. illetve 3. részfeladat a zárójelek elhagyásával bõvíthetõ.

Eddig túlságosan az egész típusú tömbökre összpontosítottunk, pedig a tömb által tárolt értékhalmaz (a tömb alaptípusa) természetesen akármilyen Jáva típus lehet. Lehet természetesen objektumreferencia is és ez agy nagyszerû esélyt ad nekünk arra, hogy objektumokat nagy számban állí tsunk elõ. Eddig ugye mindegyikhez kellett egy külön referenciaváltozó, de ha most egy tömböt csinálunk referenciákból, jó sok objektumot létrehozhatunk. Emlékszünk még Elso objektumunkra a 7. fejezetben? Most csinálhatunk belõle mondjuk százat olymódon, hogy létrehozunk egy Elso típusú referenciatömböt és minden tömbelembe belepakolunk egy új Elso egyed referenciáját. Valahogy í gy:

Elso et[] = new Elso[100]; for( int i = 0 ; i < 100 ; ++i ) et[i] = new Elso();

Az eredmény száz Elso egyed amelyeket az "et" tömb megfelelõ indexszelésével érhetünk el. Például mondhatjuk:

Page 50: Bevezetes a Java Programozasba

et[42].prog( args );

Ekkor a 43. Elso egyed prog metódusát hí vtuk meg args paraméterrel.

Feladat

Í rd át népszerû Kõ-Papír-Olló programunkat a 7. fejezetbõl úgy, hogy 5 játékos játsszon! A Jatekos referenciákból csinálj egy tömböt és ciklusokkal oldd meg. amit az eredeti program külön változókkal csinált! Í me a megoldás. Vedd észre, hogy a megoldás rövidebb, mint az eredeti program, tömbökkel sokat lehet spórolni!

Mint mondtam, a tömb alaptí pusa bármi lehet, tehát tömb is. A tömb alaptípusú tömböt többdimenziós tömbnek hí vjuk és néha nagyon jól tudnak jönni. A példa kedvéért tekintsünk egy kétdimenziós tömböt.

double t[][] = new double[100][100];

Ebben 100x100 double elem van, minden double elem 8 bájtot foglal, tehát a tömb mérete 80 kilobájt meg egy pici. Ez már igazán hatékony eszköz. Cí mezni a többdimenziós tömböt is csak úgy kell, mint egy egydimenzióst:

t[42][45] = 3.14;

Feladat

Í rjunk programot, ami kiszámolja a hõeloszlást ví zzel teli csõben. Azt tudjuk, hogy csõ keresztmetszete négyzetes, az alsó oldala egyenletes 12 fokon, a teteje egyenletes 90 fokon van, az oldalfalai mellett pedig a hõmérséklet egyenletesen 12-rõl 90 fokra emelkedik. Oldjuk meg a feladatot olymódon, hogy a csõ keresztmetszetét bontsuk kis kockákra, mondjuk 100x100-ra. A legszélsõ kockákat (tehát a 0. és 99. sorban ill. a 0. és 99. oszlopban levõket) töltsük fel a fix hõmérsékletértékekkel, a többit meg tetszõleges értékkel, pl. 12 és 90 átlagával. Számoljuk újra minden nem fix hõmérsékletû kiskocka hõmérsékletét úgy, hogy az a négy szomszédjának az átlaga, írjuk vissza az átlagot a kiskockába és tartsuk számon, mekkora volt a legnagyobb változás az átlagszámítás elõtti és utáni értékekre vonatkoztatva. Ha egy menetben a legnagyobb hõmérsékletváltozás nem több, mint 0.01 fok, nyomtassuk ki a tömbben tárolt értékeket 4 soronként ill oszloponként. Í me a megoldás. Mit befolyásol az átlagoláson átesett kiskockák kezdõértéke?

Ellenõrzõ kérdések

I. Mi a tömb?

II. Mi a tömb alaptípusa?

III. Mi lehet Jávában egy tömb alaptípusa?

IV. Mit jelent az, hogy Jávában a tömböt tömbreferenciával érhetjük el?

V. Mit tárol a tömb length egyedváltozója?

VI. Mit jelent, hogy egy tömb objektumrefernciákat tárol?

Page 51: Bevezetes a Java Programozasba

VII. Mit jelent a többdimenziós tömb?

10. fejezet Nem csak számok vannak a világon! Dolgozzunk érdekesebb adatokkal: karakterek és azok halmazai. Karaktertípus a Jávában, a char típus. Karaktersorozatok avagy ismerkedés a String osztállyal. String és StringBuffer, a két jóbarát.

Talán már kicsit unalmas, hogy példáink mindig számokról szólnak. Ugyan szövegeket állandóan írunk ki, de nem tudunk velük olyan könnyedén variálni, mint a számokkal. Ezen lecke végére ez a hiányérzetünk is megszûnik, mert most a Jáva karakterkezelési eszközeivel foglalkozunk. A számítógép ugyanolyan könnyedén tárol és manipulál karakter típusú adatokat (emlékszel az elsõ leckére? A karakterek a számítógép által ábrázolható összes betû, számjegy, jel összessége), mint számokat. Számára természetesen ezen adatoknak semmi jelentése nincsen, az egymás után tárolt a,l,m és a betûk semmilyen módon nem idézik fel benne a gyümölcsöt, mint ahogy a 2001 értékû egész típusú szám sem jelenti számára a mostani évet. Minthogy a számítógép nem magának számol, hanem nekünk, így aztán elég, ha a karaktereknek csak számunkra van jelentése.

A karakterek tárolására szolgáló alaptípus a char. Egy char típusú változó egy karaktert képes tárolni. Értékül létezõ karakter típusú érték vagy karakterkonstans adható. Példa:

char c; c = 'A';

A c változó értéke most tehát az 'A' karakter, egészen pontosan annak a kódja. A számítógép számokkal képes dolgozni, í gy a karaktereket is számokként ábrázolja, minden karakternek saját kódja van. A számítógépes gyakorlatban millióféle karakterkódolás létezik, a Jáva belsõleg a Unicode karakterkészletet használja. A Unicode Jáva által használt változata nem egy, hanem két bájton ír le egy karaktert, így több, mint 65000 karaktert képes ábrázolni. A fenti gondolatmenet eredménye az, hogy char és egész típusok egymásba alakíthatók, minthogy mindegyik csak egész szám. Példa:

int i = (int)c; char d = (char)i;

Feladat

Í rj egy programot, ami kiírja a Jáva karakterkészletet 32 és 127 között, a határokat beleértve. Egy sorba írd ki a karakter kódját majd a karaktert magát is és ezt végezd el az összes kódra. Nagyon egyszerû feladat, remélhetõen menni fog, ha nem, itt van a megoldás.

Page 52: Bevezetes a Java Programozasba

Igazában az esetek többségében nem egyedi karakterek érdekelnek, hanem azokból összeállított sorozatok, karaktersorozatok vagyis népszerû angol nevükön stringek. Stringet könnyedén csinálhatnánk karakterekbõl készített tömb segítségével

char string[] = new char[20];

a Jáva azonban elõzékenyen rendelkezésünkre bocsátja a String osztályt, teljes nevén java.lang.String-et. A csomagokról, mint például a java.lang majd a következõ fejezetben olvasunk, most elég annyi, hogy ez az osztály a beépített osztálykönyvtár része és alapból a rendelkezésünkre áll, nem kell érte semmit tennünk, hogy felbukkanjon. A String egy nem megváltoztatható karaktersorozat. Ez azt jelenti, hogy egy Stringet ha egyszer létrehoztunk adott szöveggel, akkor a szöveg a továbbiakban nem változtatható meg. Ez nem tûnik túl í géretesnek, viszont a megváltoztathatatlan String egyedet felhasználhatjuk további Stringek építésére.

Stringet lértehozni nagyon egyszerû.

String s = new String( "Hello, Jáva" );

A String tehát egy mezei objektum, olyan, amilyeneket eddig láttunk, kivéve, hogy nem nekünk kellett definiálnunk, hanem már megtették helyettünk. Az s egy String egyedre mutató referencia, ilyet is láttunk már. Egy trükk van a képben: a "Helló, Jáva" konstans. A String egy kivételes osztály a Jávában abban a tekintetben, hogy String típusú konstansokat a nyelv közvetlenül támogat. A "Helló, Jáva" igazából létrehoz egy String egyedet, amiben a "Helló, Jáva" string van, majd ennek alapján a new String létrehoz egy új string egyedet. Nem túl hatékony, én is csak azért írtam le, hogy határozottan megmutassam, hogy a String is csak egy objektum. Igazából elegendõ lenne ennyi:

String s = "Hello, Jáva";

Nézzük, mit is csináltunk elõbb. Itt a soha vissza nem térõ alkalom, hogy egy pillantást vessünk a Jáva könyvtár leí rására, ami elérhetõ a weben vagy letölthetõ a Sun-tól a JDK weblapjáról (a cikk í rásának a pillanatában a Java 2 1.3-as változata a legújabb. Keressük meg innen kiindulva a JDK API dokumentációját és nézzük a String lapját! Megtalálhatjuk a választ, hogyan mûködött az elsõ sor. A Stringnek van egy konstruktora, ami Stringet fogad.

String(String value);

Ezt használtuk fel az imént. Két sorra bontva:

String t = "Hello, Jáva"; String s = new String( t );

De sokat beszéltünk errõl az egyszerû sorról, nézzünk valami érdekesebbet! Tekintsük a következõ jól ismert sort:

public static void main( String args[] ) ...

Hát igen, ez a main definíciója. Most már megérthetjük a paraméter részét is: a main egy Stringekbõl álló tömböt kap, mindegyik Stringben egy paraméter. Ha tehát a programot í gy hí vjuk meg:

java Program P1 P2 P3 P4

Page 53: Bevezetes a Java Programozasba

akkor a main egy 4-elemû tömböt fog kapni, a tömb elemeiben egy-egy String egyedre van referencia, és ezek a String egyedek sorban a P1, P2, P3 és P4 karaktersorozatokat tárolják. Amint a tömböknél tanultuk, minden tömbnek van egy length nevû egyedváltozója, ami megadja a tömb hosszát. Az összes paraméter kií rása ezek után nem nehéz:

for( int i = 0 ; i < args.length ; ++i ) System.out.println( args[i] );

Í rjunk most rövid programot, ami kiírja az összes paraméterét, amiben megtalálható az "al" karaktersorozat. Tehát ezeket mind kiírja: alma, fal, falu de nem írja ki az akol vagy az olaj szavakat. Alaposan végigtanulmányozva a String metódusainak leí rását, megtaláljuk az indexOf(String str) metódust, ami megkeresi a String egyedben, amire meghí vták a paraméterül kapott stringet és megmondja a pozícióját ill. -1-et ad vissza, ha nem találja. A program igazán egyszerû, de itt a megoldás ellenõrzésképpen. Ha megvan, írd át úgy a programot, hogy a keresett részletet kiemeli úgy, hogy elé és mögé _ jeleket tesz, pl. _al_ma, t_al. Ehhez a substring metódusra lesz szükséged, ellenõrizheted a megoldást itt.

Szép dolog, hogy van egy megváltoztathatatlan String-ünk, de sokkal érdekesebb lenne, ha felhasználhatnánk más String-ek alkotására. Két String-bõl egy harmadikat csinálni Jávában nagyon egyszerû, mert String objektumokra definiálva van a + operátor és összefûzést jelent. Példa:

String s1 = "eleje"; String s2 = "vége"; String s3 = s1 + s2;

A dolog végén s3 az "elejevége" szöveget fogja tartalmazni. Egy baja van ennek a megoldásnak: ha sokat használjuk, jócskán teleszemeteli a memóriát, minthogy minden lépésben egy nem változtatható String keletkezik. A Jáva készítõi a változtatható StringBuffer könyvtári osztállyal oldották meg a problémát.

A StringBuffer arra való, amire a String nem alkalmas: karaktersorozatok manipulálására. A StringBuffer képes a benne levõ karaktersorozat végére egy másikat fûzni, törölni, felülírni a tartalom egy részét vagy beszúrni tetszõleges pontra. A könnyedségnek persze ára van: a StringBuffer több helyet foglal, mint egy String. A legjobb, ha mindkettõt a helyén használjuk és ez nem nehéz, mert mindkettõnek van olyan konstruktora, amely Stringet vagy StringBuffert tud csinálni a másikból. Mondhatjuk tehát:

String s = "Hello"; StringBuffer sb = new StringBuffer( s ); String s2 = new String( sb );

Igazából a Jáva fordító a Stringek összefûzését is StringBufferrel oldja meg: eleje-vége példánknál elõször egy ideiglenes StringBuffer jön létre, ebben összefûzõdik a két String, majd ebbõl legyártódik az s3-ba kerülõ eredménystring. Ha mondjuk egy jóféle hurokban tesszük ezt, könnyedén legyárthatunk néhány száz ideiglenes objektumot.

Feladat

Page 54: Bevezetes a Java Programozasba

Í rj egy programot, ami összefûzi a paramétereit egy Stringgé és ezt a Stringet kiírja. A paraméterek között _ jelek legyenek. Hozz létre egy StringBuffert, ebben fûzd össze a paramétereket, majd az eredményt alakítsd Stringgé és írd ki! Í me a megoldás ellenõrzésképpen.

Ellenõrzõ kérdések

I. Mi a karakter?

II. Hányféle jelet képes tárolni a Jáva char típus?

III. Hogy hí vják a Jáva által támogatott karaktertípust?

IV. Mi a karaktersorozat (string?)

V. Mit jelent, hogy a String nem megváltoztatható?

VI. Hogyan lehet egy Stringnek kezdõértéket adni?

VII. Mire való a String indexOf metódusa?

VIII. Mire való String substring metódusa?

IX. Mi a különbség a StringBuffer és a String között?

11. fejezet A Jáva osztályok is csak fájlok; Jáva osztályok elhelyezése és fellelése. További káoszteremtõ eszközök: package és import. Jó helyek a fájlrendszeren: a CLASSPATH környezeti változó.

Ha tényleg megcsináltad a feladatokat és nem csak végigfutottál a megoldásokon (ebben az esetben nem fogsz megtanulni Jávául), a játékterül szolgáló könyvtáradban jókora zûrzavar lehet már. Az Elso.java-t már legalább háromszor felülírtad különbözõ változatokkal és ki tudja, mi mûködik ott és mi nem. Ha néhány Jáva gyakorlóprogrammal ekkora zûrzavart lehet csinálni, vajon mire lehet képes egy csapat termelékeny programozó? Világos: végtelen káoszra. A káosz látszólagos rendezettségének növelésére a Jáva bevezeti a csomag (package) fogalmát.

A csomag vészesen analóg a fájloknál megszokott fájlnév-könytárnév szerkezettel. Minden fájlnak van egy neve, de ez még kevés az eléréséhez; általában tudni kell azt is, milyen könyvtárban van. Hasonlóképpen van ez a Jáva osztályokkal; általában nem elég, ha a nevüket tudjuk, tudni kell, milyen csomagban vannak. Í me néhány példa Jáva osztálynevekre csomagnevekkel kibõvítve.

java.awt.color.ColorSpace java.util.Vector pelda.Elso

Page 55: Bevezetes a Java Programozasba

Az elsõ kettõ egy-egy ténylegesen létezõ Jáva könyvtári osztályt jelöl. Vegyük a másodikat: ez a java.util csomagban elhelyezkedõ Vector osztály. A harmadik az én agyam szüleménye, de attól még létezhet. Mindjárt meglátjuk, hogyan!

Ha a Jáva forrásfájlba betesszük a package direktívát, a fájlban mögötte deklarált osztályokat abba a csomagba teszi. pl.

package a.b; class C { ... }

A fenti varázslat eredménye az, hogy az osztály neve nem egyszerûen C, hanem a.b.C lesz, vagyis az a.b csomagban található.

Mindez nagyon egyszerû, de mi hasznunk ebbõl? Hogyan lehet elérni csodálatos a.b.C osztályunkat? Mi sem egyszerûbb ennél. Ha egy osztályra referenciát akarunk gyártani, az osztály nevét kell használni típusnak. Í gy ni:

a.b.C ref = new a.b.C();

Egyszerû és elegáns, nemdebár? Egy apró probléma van a dologgal: elõbb-utóbb bele fogunk unni hosszú nevû osztályunk nevének gépelésébe, pláne, ha nem a.b.C az osztály neve, hanem a fent megemlített java.awt.color.ColorSpace és ezzel még egy szelídebb példát választottam. Minthogy a programozók lusta népek, meg lehet ezt úszni kevesebb gépelésbõl is. Ha a programunk elejére odaírjuk az import direktívát, megadhatjuk, milyen osztályoknál vagy csomagoknál nem akarjuk kiírni a teljes nevet. Például mondhatjuk azt, hogy

import a.b.C;

Ezzel kijelentettük, hogy az a.b.C osztályra egyszerûen, mint C-re kívánunk hivatkozni ebben a forrásfájlban. Leírhatjuk, hogy

C ref = new C();

Mindjárt barátságosabb. Ennél többet is tehetünk, azt is mondhatjuk, hogy a csomagban található összes osztályt egyszerûsített formában akarjuk látni.

import a.b.*;

Ez az a.b csomag összes osztályát egyszerûsített formában teszi elérhetõvé.

Nagyon fontos megjegyezni, hogy az import direktí va csupán azt befolyásolja, hogyan oldja fel a fordító az osztályneveket. Akármit is varázsolunk, a lefordított kódba mindenképpen az a.b.C-re való hivatkozás kerül be, import ide vagy oda. Az import ugyancsak nem tölt be semmit se a tárba, mint azt naí van hinnénk. Szerepe csupán a gépelés csökkentésében van, futásidõben semmit se számít.

Még egy fontos megjegyzés: a fordító minden Jáva forrásfájl elejére odaképzel egy import java.lang.*; direktívát, így a java.lang csomagot mindig importálja. A java.lang csomagban régi

Page 56: Bevezetes a Java Programozasba

barátaink vannak, pl. a java.lang.Integer, aminek parseInt metódusához oly sok kedves élményünk fûzõdik, vagy a java.lang.Math, amivel a kapcsolat már tényleg mélyebbé vált egyszerû barátságnál.

De vissza a package-re! Tekintsük a következõ programot! Mi lenne ez más, mint népszerû Elso programunknak egy egyszerûsített változata azzal a csavarral, hogy a programot befoglaló osztályt pelda.Elso-nek neveztük el. Fordítsd le fürgén a javac Elso.java paranccsal, majd futtasd le a java pelda.Elso utasítással. (ne feledd: a java parancsnak a futtatandó osztály nevét kell megadni, ebben az esetben pelda.Elso-t). Ilyen egyszerû egy osztályt csomagba tenni.

Eeeee ... valami nem stimmel? A java pelda.Elso hatására nem az unalomig ismert kií rás jelent meg, hanem az, hogy a pelda/Elso osztály nem található. Mindez arra világít rá, hogy még mindig nem tudunk mindent az osztályok betöltõdésérõl.

Eddig ezzel nem sokat törõdtünk. A lefordított .class fájlokat az aktuális könyvtárban tároltuk és elvártuk, hogy a virtuális gép megtalálja õket. A csomagok bevezetésével a helyzet megváltozik és meg kell ismerkednünk egy osztályt tartalmazó .class fájl fellelésének módjával. Amikor a virtuális gép meglát egy osztálynevet, azonnal elkezdi keresni a CLASSPATH környezeti változó által megadott könyvtárakban. Ha esetleg nem tudnád, mi a környezeti változó, í me egy kis bevezetõ:

2. kitérõ: környezeti változók

A Jáva a CLASSPATH környezeti változót használja a következõ módon. A CLASSPATH értéke könytárak sorából áll (Windows-on pontosvesszõvel, Unix-on kettõsponttal elválasztva). Amikor a rendszer keresni kezdi az a.b.C nevû osztályt, veszi az elsõ könyvtárat a CLASSPATH-en. Ehhez hozzáfûzi a csomag nevét, í gy lesz egy alkönytárnév és ebben keresi meg az osztálynév.class fájlt. Ha nem találja, továbblép a következõ könyvtárra a CLASSPATH-en és ezt addig csinálja, amí g a könyvtárak a CLASSPATH-en el nem fogynak. Ha nem tudta megtalálni azt osztályt a CLASSPATH-en felsorolt összes könyvtárban, hibajelzést küld.

Lássunk egy egyszerû példát! Tegyük fel, hogy a CLASSPATH értéke a következõ:

c:\Users\javadev;c:\Users\javalib

Tegyük fel továbbá, hogy a rendszer az a.b.C nevû osztályt keresi. Veszi tehát az elsõ könyvtárat a CLASSPATH-en, a c:\Users\javadev-et. Ehhez hozzáilleszti a csomagnevet, vagyis az a.b-t és kapja a c:\Users\javadev\a\b könyvtárat. Itt megpróbálja fellelni a C.class fájlt. Tegyük fel, hogy nem találta meg. Ekkor továbblép a c:\Users\javalib-re és megpróbálja ugyanezt, vagyis felkeresi a c:\Users\javalib\a\b\C.class fájlt. Ha ez megvan, siker. Ha nem, hibaüzenet.

Hosszadalmas magyarázat után vissza a pelda.Elso alkalmazásra! Ha figyelmesen megtanulmányozod a fájlokat a könyvtáradban, rájöhetsz, hol a hiba: a Jáva fordító az aktuális könyvtárba pakolta le a .class fájlt. Ez egy hülye tulajdonsága a javac-nak, amely a Jáva elsõ változata óta kísért. Tedd helyre olymódon, hogy létrehozol egy pelda alkönyvtárat és átmozgatod az Elso.class-t ebbe. Tedd meg! Ha most kiadod a java pelda.Elso parancsot, két dolog történhet: vagy mûködik, vagy nem. Sohase tudtam rájönni, miért, de bizonyos Jáva installációk ilyenkor képtelenek megtalálni a kurrens könyvtárban elhelyezkedõ alcsomagokat, csak ha varázsolunk a CLASSPATH-szel. He netán ez a hiba ütné fel undok fejét, vedd fel az aktuális könyvtárat a CLASSPATH-re.

set CLASSPATH=.

Page 57: Bevezetes a Java Programozasba

mondd a parancspromptnál Windows-on. Ha most megismétled a parancsot, mûködnie kell.

A javac-nak a tulajdonsága, miszerint mindenképpen az aktuális könytárba pakolja a .class fájlt, még ha az valamilyen csomagba fordítódik igazán kellemetlen és erre a Sun-nál is rájöttek. A Javac-ot meghí vhatod a nagyszerû -d kapcsolóval, ekkor létrehozza a csomagok alkönyvtárait és mindent a helyére pakol. Próbáld ki! Töröld le az elõbb létrehozott pelda alkönyvtárat a benne levõ Elso.class-szel együtt, majd mondd azt:

javac -d . Elso.java

ekkor a pelda alkönyvtár rendben létrejön az aktuális könyvtárban és belekerül az Elso.class. Ekkor már probléma nélkül lehet futtatni a java pelda.Elso paranccsal.

A csomagok kezelésének a lehetõsége apróság a Nagy Egészben, de igazán fontos õket ismerni, ha bele akarunk kalandozni a Jáva osztálykönyvtárba.

Ellenõrzõ kérdések

I. Mi a hasonlóság az alkönyvtárak és a csomagok között?

II. Hogyan kell egy Jáva osztályt egy adott csomagba tenni?

III. Mit jelent a package direktíva és mi a paramétere?

IV. Hogyan lehet egy csomagban levõ osztályt elérni?

V. Mit egyszerûsít az import direktíva?

VI. Hogyan leli fel a virtuális gép a class fájlokat?

VII. Mi a CLASSPATH környezeti változó?

VIII. Mi történik, ha a CLASSPATH értéke c:\Users\javalib;c:\Users\enyem és az a.b.C nevû osztályt keressük?

IX. Hova teszi a javac a lefordított osztályokat, ha egyszerûen csak a forrásfájl nevével hí vjuk meg?

X. Hogyan lehet elérni, hogy a javac a csomagokat a megfelelõ alkönyvtárba tegye?

12. fejezet Mindenki a saját hibáinak kovácsa: személyre szabott hibajelzések a Jávában. Kivételek élete és halála: throw utasítás, a throws kulcsszó valamint a try-catch blokk.

Feladat

Page 58: Bevezetes a Java Programozasba

Í rjunk programot, ami kiírja a bementi paraméter faktoriálisát. Ha emlékszel, n faktoriálisa az 1-tõl n-ig terjedõ egész számok szorzata, 0! = 1. Negatív számokra a faktoriális nincs értelmezve. A faktoriális értékek rendkí vül gyorsan nõnek, í gy 20!-nál nagyobb értékek a long típusból (az int 64 bites változatából) is kilógnak. Í rj egy függvényt, amelyiket meg lehet hí vni egy egész számmal és visszaadja a faktoriális értéket! A függvény adjon vissza hibajelzést, ha érvénytelen bemeneti értéket kapott. A main függvény hí vja meg ezt a faktoriálisszámító függvényt az args[0]-ban kapott bemeneti paraméterrel (Integer.parseInt ...) majd írja ki a hibaüzenetet, ha a faktoriálisszámító függvény hibajelzést adott vissza, különben írja ki az eredményt. A feladat nem okozhat gondot, í me a megoldás.

A programban valóban nincs semmi újdonság. Ugye, a 9. lecke után nem lep meg, hogy a fakt függvénynek szintén statikusnak kell lennie? (mivel a statikus main-ból hí vjuk) A fakt függvény önmaga nem írhatja ki a hibajelzést (hiszen a függvényben nem tudhatjuk, egyáltalán szöveges hibajelzést kell-e adnunk, így szólt a feladat kitûzése), ezért negatív értékkel jelez hibát. Mivel a faktoriális nem lehet negatí v szám, ezt észreveheti a hí vó függvény és hibát jelezhet.

Nem túl ronda ez? De igen. Összekeveredik itt két dolog, a függvény visszatérési értéke és a hibakód. A mostani helyzet viszonylag egyszerû, mivel itt egy hibaeset van és a függvény bizonyos számokat nem adhat vissza, nehezebb lenne azonban a dolgunk, ha nem lenne ilyen "üres tartomány" a visszatérési értékek között és több hibakódunk lenne. A hibák kultúrált jelzésére a Jáva az kivétel (exception) eszközét adja.

A kivételt úgy lehet felfogni, mint egy vészféket. Ha a program futása során kivétel keletkezik (akár a rendszer generál egyet, akár mi generáljuk) a futás megszakad és a Jáva virtuális gép a kivétel feldolgozásával folytatja a tevékenységét. Hogy ez pontosan hogyan zajlik, arról egy kicsit késõbb. Most nézzük meg, hogyan generálhatunk kivételt. Ez roppant egyszerû.

throw new IllegalArgumentException();

A throw utasí tás paramétere egy kivételobjektum. A kivételobjektum pont olyan mezei objektum, mint a többi; egyetlen speciális tulajdonsága van: a java.lang.Exception könyvtári objektum leszármazottjának kell lennie. A példánkban szereplõ java.lang.IllegalArgumentException is ilyen és õ is része az alap objektumkönyvtárnak, tehát bátran használhatjuk minden további nélkül. Ugye, emlékszel, hogy a minden Jáva program elejére egy import java.lang.*; utasí tást képzel oda a fordító, barátainkat tehát nyugodtan hí vhatjuk Exception-nek és IllegalArgumentException-nek.

Kivételekbõl lehet de-luxe változatot is létrehozni, ha úgynevezett részletezõ üzenettel (detail message) hozzuk õket létre. Van ugyanis nekik egy olyan konstruktoruk, ami egy String-et fogad, itt passzolható át a részletezõ üzenet. Ennek a szerepe csupán csak annyi, hogy informaciót közvetíthet a kivételt megkapó számára, pontosan mi is volt a baj. Példa:

throw new IllegalArgumentException( "Az argumentum túl kicsi" );

A metódusban generált kivételeket mindig deklarálni kell a metódus fejlécében, erre való a throws klauza. Példa:

void func() throws IllegalArgumentException { ... throw new IllegalArgumentException();

Page 59: Bevezetes a Java Programozasba

... }

A Jáva fordítónak mindig megvan a módja rá, hogy összehasonlítsa a metódusban dobott kivételeket a throws után felsoroltakkal (vesszõvel elválasztva tetszõleges számú kivételt felsorolhatunk) és ha eltérést lát, a maga egyenes, de kissé nyers módján hibaüzenetet küld. Próbáld ki!

Feladat

Í rd át a faktoriálisszámító programot úgy, hogy a fakt metódus IllegalArgumentException-nel jelezze, ha a metódus hibás bemeneti paramétert kapott. Emitt a megoldas. Futtasd le a programot néhány hibás bemenõ értékkel és ellenõrizd az eredményt!

Ilyen választ fogsz kapni: Exception in thread "main" java.lang.IllegalArgumentException: Tul kicsi bemeneti parameter at Faktorialis2.fakt(Faktorialis2.java:5) at Faktorialis2.main(Faktorialis2.java:15)

Ez egy hí vási verem lista. Azt mondja meg, hogy az elsõ sorban részletezett kivétel a Faktorialis2 osztály fakt metódusában keletkezett, majd továbbterjedt a main metódusba (methogy a fakt-ot innen hí vták meg) és ezzel elérte a hí vási verem tetejét, a program futását megszakította. Ez azért volt lehetséges, mert primkó programunkban nem okozott gondot, hogy a kivétel megszakítsa a program futását. Normális esetben azonban ennél kifinomultabb kivételkezelésre van szükség és erre való a try-catch blokk.

A try-catch szintaktikája a következõképpen néz ki:

try { ... utasítások ... } catch( Exception1 e1 ) { ... Exception1 típusú hibát lekezelõ utasítások ... } catch( Exception2 e2 ) { ... Exception2 típusú hibát lekezelõ utasítások ... } finally { ... Az egész blokk legvégén (hiba bekövetkezésétõl függetlenül) végrehajtódó utasítások ... }

A try után bekövetkezõ blokkban vannak az utasí tások, amelyek Exception1 vagy Exception2 típusú hibát generálnak. A fordító ellenõrizni fogja, tényleg megvan-e erre a lehetõség (minthogy a meghí vott metódusok throws klauzájából tudja, azok milyen kivételt dobhatnak), ha valamelyik kivétel nem következhet be, hibaüzenetet ad. Ha nem történik hiba, a try blokk rendben végrehajtódik és a finally mögött levõ blokkal folytatódik a program futása. Ezen blokk végrehajtása után a szerkezet végetér.

Ha azonban a try blokkban kivétel következik be, a blokk futása megszakad és a kivételobjektum (amit a throw-val generáltunk) beíródik a megfelelõ catch blokk referenciaváltozójába. Ha például Exception1 következett be, az e1 mutat a kivételobjektumra, amikor a catch blokkba kerülünk és a futás az Exception1 catch blokkjában folytatódik. Ezt elvégezve a finally blokkja kerül sorra, majd

Page 60: Bevezetes a Java Programozasba

elhagyjuk a szerkezetet. Egynél több catch blokk és a finally blokk opcionális. Legegyszerûbb formájában a szerkezet í gy néz ki:

try { ... utasítások ... } catch( Exception1 e1 } { ... Exception1 típusú hibát lekezelõ utasítások ... }

Mostanra eljutottunk oda, hogy le tudjuk írni, pontosan mi történik egy kivétel keletkezésekor. Kivétel keletkezésekor a program futása megszakad és megszakad a hí vóé is, ha nem definiált try-catch blokkot a kivételre, hanem a throws klauzával azt jelezte, hogy tovább akarja dobni. Í gy megy ez addig, amí g a hí vási lánc tetejére nem érünk (mint elõzõ példánkban) vagy bele nem ütközünk egy aktív trí -catch blokkba. Ekkor a try blokk megszakad és a futás a megfelelõ catch blokkon folytatódik. A fentiekbõl következik, hogy ha egy metódust meghí vunk, ami dobhat valami kivételt és ezt jelzi is a throws klauzájában, akkor két eset lehetséges: vagy elkapjuk try-catch-csel, vagy továbbdobjuk throws-szal. Harmadik eset nincs, az összes további próbálkozás szintaktikai hiba.

Egy teljesen logikus dolgot megemlítenék, amiba kezdõ, de még tapasztaltabb Jáva programozók is beleesnek: a try blokk minden utasí tását úgy tekinti a fordító, hogy az esetleg nem hajtódik végre, ezért a try blokkban elkövetett változóinicializálásokat nem veszi figyelembe, amikor azt nézi, hogy inicializálták-e a változót. A következõ tehát szintaktikai hiba:

int i; try { i = 1; ... } catch( Exception1 ex ) { ... } System.out.println( "i: "+i );

A System.out.println soránál a fordító figyelmeztetni fog, hogy az i változót nem inicializáltuk. Ezt a megnyugtatására meg kell tennünk, tehát int i = 0; szükséges.

Mielõtt leckénk zárófeladatához érkeznénk, még egy pár szót a nem jelzett kivételekrõl. Ezek valóban kivételesek, mert általában rendszerszintû programrészek generálják õket gépi kódú részekbõl. Sok esetben nincsenek throws-szal jelölve, hiszen nagyon sokféle utasí tás végrehajtásakor keletkezhetnek. Ilyen pl. minden Jáva programozó legõszintébb barátja, a NullPointerException, ami akkor lesz, ha null értékû referenciát akarunk felhasználni egy objektumhivatkozásban. Ezt nem metódusok dobják, hanem ártalmatlan sorok pl. ref.valtozo formájú hivatkozás. Nem jelzett kivételeket is elkaphatunk, ha a problémás környékre egy try-catch blokkot telepítünk a catch ágban minden kivétel õsével, az Exception típussal. Példa:

Object o = null; try { o.toString(); } catch( Exception ex ) { ... NullPointerException-t lekezelõ programrész ... }

Feladat

Page 61: Bevezetes a Java Programozasba

Bõvítsd ki elõzõ programunkat úgy, hogy a keletkezõ kivételt a main-ben kapd el és írd ki valami kultúrált formában. Használd az Exception objektum getMessage() metódusát, amivel megszerezheted a részletezõ üzenetet a catch blokkban. Emitt a megoldás.

Ellenõrzõ kérdések

I. Mi a kivétel?

II. Hogyan lehet kivételeket létrehozni?

III. Kell-e deklarálni a metódusban létrehozott kivételeket és ha igen, hogyan?

IV. Mire szolgál a try-catch?

V. Hány catch ága lehet egy try-catch szerkezetnek?

VI. Hány finally ága lehet egy try-catch szerkezetnek?

VII. Mi a teendõ, ha metódusunk olyan másik metódust hí v meg, ami kivételt generálhat?

VIII. Mik a nem jelzett kivételek és hogyan lehet elkapni õket?