Java Programozás 5. Ea: OOP alapok Abstract Javaságok 89/1 B ITv: MAN 2018.03.13
89/2
OOP alapelvek Adatok és a hozzájuk kapcsolódó tevékenységek egységbe zárása (encapsulation) Alapegység: osztály, melynek építőkövei: adattagok, metódusok. Információ rejtés: Egy objektum adatai a külvilág számára hozzáférhetetlenek. Egy objektum a külvilággal csak az interfészén keresztül tarthatja a kapcsolatot. (Interface: a külvilág számára elérhető módszerek metódusok együttese.) A módszerek implementációja rejtett. Öröklődés (inheritance): az osztályok egy részét már meglévő osztályok továbbfejlesztésével hozzuk létre, úgy, hogy további adattagokkal, illetve metódusokkal bővítjük. Többalakúság (polimorphism): A származtatott osztályokban lehetnek ugyanolyan elnevezésű, de más tevékenységű metódusok, mint az ős osztályban => áttekinthető kód, nem kell az 89/3 elnevezéseket variálnunk.
További OOP alapfogalmak Osztály: Objektumok közös tulajdonságait definiálja. Programozástechnika szempontból egy típus. Építőkövei: adattagok (attribútumok, jellemzők, tulajdonságok), metódusok (műveletek, viselkedés). Objektum: Egy osztály egy működőképes példánya. Egy adott osztályban definiált tulajdonságokkal tetszőleges számú objektum példányosítható. Minden objektum természeténél foga különbözik az összes többitől. Egy adott osztályból példányosított valamennyi objektumnak ugyanolyan lehetséges viselkedési módjai vannak, de mindnek saját állapota van. Üzenet: Az objektummal való kommunikáció módja. A módszerek (metódusok) aktivizálását jelenti. 89/4
89/5
Öröklődés Fogalma: új osztály létrehozása (gyermek) egy már meglévő osztályt (szülő) felhasználva (kód újrafelhasználás). A gyermek osztály örökli a szülő osztály összes tagját, - kivéve a konstruktorokat és az inicializáló blokkokat -, definiálhat új tagokat és módosíthat örökölt tagokat. 89/6
Öröklődés Gyakran előfordul, hogy egy program különböző osztályai azonos adattagokkal és metódusokkal rendelkeznek Példa: Neptun felhasználók 89/7
Öröklődés Az előző példában minden osztályban szerepel három azonos adat: 89/8 név, neptunkód, jelszó Minden osztályban szerepel a: Bejelentkezés() metódus Az osztályok ezen kívül rendelkeznek csak rájuk jellemző adattagokkal és metódusokkal is Az adattagok és metódusok ismétlődése problémákat okozhat: pazarlás metódusoknál biztosítani kell az azonos működést a módosításokat több helyen át kell vezetni. A megoldás: öröklődés
Öröklődés A közös adatok és metódusok egy ős osztályba gyűjthetők. Ennek neve legyen NeptunFelhasznaló Az egyes felhasználó osztályok ezek leszármazottjaiként deklarálhatók. A leszármazottak öröklik a közös tagokat. Ezzel kód megosztás jön létre: a leszármazottak használhatják a szülő osztály metódusait. 89/9
Öröklődés Az öröklődés jele: A nyíl a leszármazott osztály felől az ősosztály felé mutat 89/10
Öröklődés Jellemzői: Egyszeres öröklés: egy gyermeknek csak egy szülője lehet. (C++-ban többszörös) Egy szülőnek több gyermeke lehet Egy gyermek szintén lehet szülő Egyetlen korlátozás: egy osztály még közvetett módon sem lehet saját maga őse Elnevezések: Ősosztály, szülő (super class, base class) Leszármazott, gyermek (subclass, derivered class) 89/11
Öröklődés Megadása: class osztálynév extends ősosztály { 89/12 Ha nem adunk meg szülő osztályt, akkor automatikusan a java.lang.object osztály a szülő. Az Object tehát minden osztály őse, tagjait minden osztály örökli! A gyermek osztály örökli szülője tagjait, de: a private tagokhoz nem fér hozzá, a félnyilvános tagokhoz pedig csak akkor, ha ugyanabban a csomagban van (ezeket a szülő osztály hozzáférhető tagjainak segítségével kezelheti) a protected, public tagokhoz közvetlenül hozzáfér.
Konstruktorok az öröklődés során A konstruktor nem öröklődik. Mind az ős osztály, mind a leszármazott osztály rendelkezhet konstruktorral (akár többel is). Egy leszármazott objektum példányosításánál tisztázni kell: 89/13 A konstruktorok végrehajtási sorrendjét Azt, hogy hogyan választhatjuk ki az ősosztály konstruktorai közül a végrehajtandót Végrehajtási sorrend: először mindig az ősosztály, majd a leszármazott osztály konstruktora hajtódik végre. A pontos sorrend: Az ős osztály adattagjainak inicializálása Az ős osztály konstruktorának végrehajtódása A gyermek osztály adattagjainak inicalizálása A gyermek osztály konstruktorának végrehajtódása
Konstruktorok az öröklődés során Az ős osztály konstruktorának kijelölése: 89/14 A gyermek osztály első sorában szerepelhet egy super(paraméterek) konstruktorhívás. A paraméterlistának az ős osztály valamelyik konstruktorára illeszkednie kell. Ha ilyen hívás nem szerepel a gyermek osztály konstruktorában, akkor egy implicit super() hívással kezdődik a konstruktor végrehajtása. Következmények: ha a gyermek osztálynak van olyan konstruktora, amelyben nincs explicit ős konstruktor hívás, a szülő osztálynak kell legyen paraméter nélküli konstruktora. Ha a gyermek osztálynak csak implicit konstruktora van, az is az ős osztály paraméter nélküli konstruktorát hívja meg.
public class Szemely { String nev; int kor; Dolgozo d = new Dolgozo("tanár", 550); 3 public Szemely(String nev, int kor){ this.nev = nev; this.kor = kor; public Szemely(){ 1 this("kis Béla", 25); 4 2 public class Dolgozo extends Szemely { String munkakor; int fizetes; public Dolgozo(String munkakor,int fizetes){ super(); this.munkakor = munkakor; this.fizetes = fizetes; 89/15
Konstruktorok az öröklődés során public class Szemely { String nev; int kor; Szemely()?? NO! 3 Szemely(){ nev = null; kor = 0; 2 public class Dolgozo extends Szemely { String munkakor; 4 int fizetes; public Dolgozo(String munkakor,int fizetes){ this.munkakor = munkakor; this.fizetes = fizetes; 1 Dolgozo d = new Dolgozo("tanár", 550); 89/16
Konstruktorok az öröklődés során public class Szemely { String nev; int kor; public Szemely(String nev, int kor){ this.nev = nev; this.kor = kor; Szemely()?? NO! 2 1 public class Dolgozo extends Szemely { String munkakor; int fizetes; 89/17 public Dolgozo(String munkakor,int fizetes){ this.munkakor = munkakor; this.fizetes = fizetes;
Konstruktorok az öröklődés során public class Szemely { String nev; int kor; {nev = "Kis Béla"; kor = 25; public Szemely(String nev, int kor){ this.nev = nev; this.kor = kor; Szemely()?? NO! 2 1. Adattagok deklarálása 2. Inicializálás 3. Konstruktor végrehajtás 4. A változó referenciájának beállítása 1 public class Dolgozo extends Szemely { String munkakor; int fizetes; 89/18 public Dolgozo(String munkakor,int fizetes){ this.munkakor = munkakor; this.fizetes = fizetes;
Öröklődés Adattagok öröklése: A leszármazott örökli az összes adattagot, és új adattagokat is definiálhat Definiálhat egy örökölt adattaggal megegyező nevű adattagot is, ilyenkor az örökölt tagot elrejti (hide) Az elrejtett tagra a super kulcsszóval hivatkozhatunk: super.adattag super.super.adattag hívás nem lehetséges! 89/19
public class Szemely { String nev; int fizetes; public Szemely(String nev, int fizetes){ this.nev = nev; this.fizetes = fizetes; Adattagok öröklődéskor public class Dolgozo extends Szemely { int fizetes; public Dolgozo(String nev, int fiz1, int fiz2){ super(nev, fiz1); fizetes = fiz2; public class OuterP { public static void main(string args[]) { Dolgozo d = new Dolgozo("Béla", 550, 200); System.out.println(d.nev+" - "+d.fizetes); Szemely sz = d; System.out.println(sz.nev+" - "+sz.fizetes); 89/20
public class Szemely { String nev; int fizetes; public Szemely(String nev, int fizetes){ this.nev = nev; this.fizetes = fizetes; Adattagok öröklődéskor public class Dolgozo extends Szemely { int fizetes; public Dolgozo(String nev, int fiz1, int fiz2){ super(nev, fiz1); fizetes = fiz2; public int getszemfiz(){ return super.fizetes; public static void main(string args[]) { Dolgozo d = new Dolgozo("Béla", 550, 200); System.out.println(d.nev+" - "+d.fizetes); 89/21 System.out.println(d.getSzemFiz());
Öröklődés Metódusok öröklése A leszármazott örökli az összes nem private metódust és újakat is definiálhat, valamint megváltoztathat örökölt metódust 89/22 Esetek: 1. Új metódus: Az örökölt metódusoktól eltérő nevű metódus definiálása 2. Új metódus örökölt metódus túlterhelésével (overloading): az egyik örökölt metódussal megegyezik a név, de eltér a paraméter szignatúra. 3. Példányszintű metódus felüldefiniálása (override): az egyik örökölt példányszintű metódussal megegyezik a szignatúra és a visszatérési érték típus Paraméter szignatúra: A formális paraméterek száma és típus-sorrendje. A visszatérési érték típusa ebből a szempontból közömbös.
Öröklődés 89/23 Metódusok öröklése Esetek (folytatás): 4. Örökölt metódus elrejtése új metódussal (hide): az örökölt osztályszintű metódussal megegyező szignatúra és visszatérési érték. 5. Hibás definiálás: Az örökölt metódustól gyengébb hatáskörű metódus (private < félnyilvános < protected < public) Az örökölt metódussal azonos szignatúrájú de eltérő visszatérési értékű metódus (hibás felüldefiniálás vagy elrejtés) Az örökölt osztályszintű metódussal azonos szignatúrájú példányszintű metódus Az örökölt példányszintű metódussal azonos szignatúrájú osztályszintű metódus final módosítójú metódussal azonos szignatúrájú metódus
Öröklődés Egy osztályhoz több metódus is tartozhat azonos névvel, de különböző paraméterszignatúrával. A metódus hívásakor a fordítóprogram az aktuális paraméterek szignatúrája alapján dönti el, hogy melyik metódust kell alkalmaznia. Ha egy megfelelőt sem talál, vagy ha többet is talál hibajelzést ad. Egy osztályhoz úgy tartozhat több azonos nevű metódus, hogy: az osztályban definiálhatók azonos nevű metódusok ugyanilyen nevű metódusokat örökölhet is az osztály A saját és örökölt metódusok együttesére érvényes az overloading szabályrendszere 89/24
Metódusnév túlterhelés public class Szemely { String név; int fizetés; int getfizetés(){ return fizetés; void fizetéstemel(int fizetés){ this.fizetés += fizetés; public class Alkalmazott extends Szemely { String beosztás; void fizetéstemel(){ fizetés += 20000; void fizetéstemel(alkalmazott a){ if (a.getfizetés() > fizetés) fizetés = a.getfizetés(); Az Alkalmazott osztályban a fizetéstemel metódus három különböző paraméterszignatúrával is használható. 89/25
Metódusnév felüldefiniálás public class Alkalmazott { String név; int nyelvekszama; public int potlek() { return nyelvekszama*20000; public class Fonok extends Alkalmazott { int beosztottakszama; public int potlek() { return super.potlek() + beosztottakszama*10000; Az ősosztály potlek() metódusának meghívása 89/26
Teszt 1. Helyes vagy hibás az alábbi kód? public class Komplex { int x, y, z; Komplex(int x, int y, int z){ this(x, y, z); 89/27
Teszt 2. Mire van lehetőségünk az utód osztályban egy osztály örökítésekor? Az adatok láthatóságát szűkíthetjük Az ősosztály adatait felüldefiniálhatjuk Új metódusokat deklarálhatunk Az ős osztály metódusait felüldefiniálhatjuk 89/28
Teszt 3. Névegyezőség esetén egy konstruktorban milyen minősítővel hivatkozhatunk az osztály adattagjaira? this() super. super() this. 89/29
Teszt 4. Mi a konstruktor? Osztályt definiáló metódus Öröklődést beállító metódus Objektumot létrehozó metódus Példányosításkor meghívódó metódus 89/30
Teszt 5. Milyen típusú konstruktor van az osztályban? public class Alma { String nev; Alma (){ Implicit Explicit 89/31
A kötés (binding) fogalma A fordítóprogram elsődleges feladata, hogy az általunk megírt forráskódot elolvassa, elemezze, megértse. Ezen elemzés közben listát vezet a kód olvasása során felismert változókról, konstansokról, típusokról, függvényekről. Az egyes programozási elemekhez rendelt nevek alapján képes felismerni melyik programsorban melyik, korábban definiált elemre hivatkozunk. Kötés-nek nevezzük azt a folyamatot, amikor a fordítóprogram a forráskód fordítása során felfedezett metódushívásokhoz konkrét metódust rendel. 89/32
A kötés (binding) fogalma A fordítóprogram elsődleges feladata, hogy az általunk megírt forráskódot elolvassa, elemezze, megértse. Ezen elemzés közben listát vezet a kód olvasása során felismert változókról, konstansokról, típusokról, függvényekről. Az egyes programozási elemekhez rendelt nevek alapján képes felismerni melyik programsorban melyik, korábban definiált elemre hivatkozunk. Kötés-nek nevezzük azt a folyamatot, amikor a fordítóprogram a forráskód fordítása során felfedezett metódushívásokhoz konkrét metódust rendel. 89/33
A kötés (binding) fogalma A kötés típusai: 89/34 Korai kötés: a fordítóprogram egy objektum statikus típusa, vagy az aktuális paraméterek statikus típusa alapján az azonosítást egyértelműen el tudja végezni. Egyedi metódusnevek Egy osztályon belül azonos nevű függvények, eltérő paraméter szignatúrával. Késői kötés: a fordítóprogram az azonosítást nem tudja elvégezni, a kötés csak futásidőben jöhet létre: Felüldefiniált metódusok, ahol metódus hívásánál dönteni kell, hogy az örökölt vagy a saját változat hívódjon meg. A döntés alapja a hivatkozás dinamikus típusa. Mivel a dinamikus típus fordítási időben nem ismert, a felüldefiniált metódusok közötti választást futásidőre kell halasztani.
Statikus és dinamikus típus Egy változónak van statikus és dinamikus típusa. Egy változó statikus típusa az, amelyet a deklarációjában megadtunk 89/35 Ez a változó teljes élete alatt változatlan Ez határozza meg, hogy milyen műveleteket végezhetünk a referenciával hivatkozott objektummal Egy változó dinamikus típusa az általa éppen hivatkozott objektum tényleges típusa. Csak olyan típus lehet, amely rendelkezik ugyanazokkal az adatokkal és műveletekkel, mint a statikus típus, ezért a változó dinamikus típusa csak a statikus típus vagy annak leszármazottja lehet. A dinamikus típus a program futása során bármikor változhat
Statikus és dinamikus típus típus név érték Alma 89/36 Gyümölcs Körte Egy gyümölcs példány lehet Gyümölcs, Alma, vagy Körte típusú. Alma példány csak Alma típusú lehet. Körte példány csak Körte típusú lehet. Egyszerű példa: miből mi lehet? Gyümölcs a, b; a = new Alma("Jonatán"); Gyümölcs a Alma Jonatán b = new Gyümölcs("Kiwi"); Gyümölcs b a = b; Gyümölcs a Gyümölcs a Gyümölcs b Gyümölcs Kiwi Gyümölcs Kiwi a = new Körte("Vilmos"); Gyümölcs a Körte Vilmos Deklarálás Statikus értékadás Példányosítás Dinamikus értékadás Értékadás Dinamikus értékváltás Újra példányosítás Dinamikus értékadás
Statikus és dinamikus típus típus név érték Gyümölcs Mi történik? Gyümölcs a = new Körte("Vilmos"); Gyümölcs a Körte Vilmos Alma Körte Körte c = new Körte("Vilmos"); Körte c Körte Vilmos Egy gyümölcs példány lehet Gyümölcs, Alma, vagy Körte típusú. Alma példány csak Alma típusú lehet. Körte példány csak Körte típusú lehet. a = c; c = a; A Gyümölcs típusú statikus változóba berakunk egy Körte típusú statikus változót! Működik? Igen, mert a Gyümölcs lehet Körte A Körte típusú statikus változóba beraknánk egy Gyümölcs típusú statikus változót! Működik? Nem, mert a Körte csak Körte lehet! 89/37 Értékadáskor változik a referencia (a dinamikus típus), de csak olyan érték adható át, amelynek a statikus típusa megfelelő!
Statikus és dinamikus típus típus név érték Gyümölcs Mi történik? Gyümölcs a = new Körte("Vilmos"); Gyümölcs a Körte Vilmos Alma Körte Körte c = new Körte("Vilmos"); Körte c Körte Vilmos Egy gyümölcs példány lehet Gyümölcs, Alma, vagy Körte típusú. Alma példány csak Alma típusú lehet. Körte példány csak Körte típusú lehet. c = (Körte)a; Ha értékadáskor a jobb oldalon álló dinamikus típus megegyezik a bal oldalon álló statikus típussal, akkor konvertálással megoldható az értékadás. 89/38 Értékadáskor változik a referencia (a dinamikus típus), de csak olyan érték adható át, amelynek a statikus típusa megfelelő!
Típuskonverzió referenciákra Automatikus konverzió: Akkor lehetséges, ha a konvertálandó statikus típusa azonos típus az eredmény statikus típusával vagy annak leszármazottja. (Biztos, hogy a konvertálandó dinamikus típusa is leszármazott vagy ugyanaz.) Kikényszerített konverzió: Gyümölcs a = new Körte("Vilmos"); Akkor alkalmazható, ha a konvertálandó statikus típusa nem azonos vagy nem leszármazottja az eredmény statikus típusának, de a dinamikus típusa igen. Az instanceof operátor a referencia dinamikus típusát vizsgálja, segítségével megvizsgálhatjuk a konverzió előtt, hogy végbemehet-e a konverzió. 89/39 Gyümölcs a = new Körte("Vilmos"); Körte c = new Körte("Vilmos"); System.out.println(a instanceof Körte); true
s 89/40
A final minősítésű adattagok A final minősítő többféle célra is használható a Java nyelvben. Jelentése mindig "valami olyasmi, ami később nem változtatható meg". Final adattagok: 89/41 Deklaráció: final típus azonosító=inicializáló_kifejezés; Az inicializáló kifejezés csak olyan elemeket tartalmazhat, amelyek az addigi deklarációk ismeretében feldolgozhatók. public class Fém { String név; final double tömeg; Fém(String név, double tömeg){ this.név = név; this.tömeg = tömeg; Miden példányosítás során végrehajtódik, tehát a változó értéke objektumként egyedi lehet. Egyetlen metódus sem változtathatja meg az értékét.
A final minősítésű adattagok Static final adattagok: deklaráció: static final típus azonosító = inicializáló_kifejezés; Az inicializáló kifejezések csak konstansokat és olyan static adattagokat tartalmazhat, amelyek már deklaráltak. Csak egyszer, az osztály inicializálása során hajtódik végre, tehát a változó értéke az osztály miden példánya számára ugyanaz. Egyetlen metódus sem változtathatja meg az értékét. public class Személy { String név; static final int minimálbér = 185000; 89/42
A final minősítésű metódusok Final metódus: Egy metódus is kaphat final minősítést. A final minősítésű metódust nem definiálhatja felül egyetlen leszármazott osztály sem. Szerepe, hogy megakadályozza bizonyos viselkedés formák megváltoztatását, ha az veszélyezteti a helyes működését. public class Kör { int sugár; final double terület(){ return sugár*sugár*math.pi; 89/43
Absztrakt metódus és osztály Gyakran előfordul a tervezés során, hogy egy osztály szintjén tudjuk, hogy valamilyen metódus szükséges lesz a leszármazottakban, de még nem lehet megadni az implementációját. Ezért a Java nyelv megengedi törzs nélküli metódus definiálását. Az ilyen metódust az abstract minősítővel kell ellátni. Ha az osztály tartalmaz absztrakt metódust, az osztályt is az abstract minősítővel kell ellátni. abstract class Termek{ private String nev; 89/44 Termek(String nev){ this.nev = new String(nev); abstract void kiir();
Absztrakt metódus és osztály Az absztrakt metódusokra vonatkozó szabályok: Absztrakt egy metódus, ha nincs törzse, megvalósítást csak a felüldefiniálás során kap. Absztrakt metódusnak nem lehet a módosítója a private, final, static hiszen az ilyen módosítókkal ellátott metódusokat nem lehet felüldefiniálni! Absztrakt egy osztály, ha van legalább egy absztrakt metódusa Absztrakt osztályt nem lehet példányosítani Egy absztrakt osztály arra szolgál, hogy ős osztálya legyen további osztályoknak A leszármazott osztály(ok) feladata az absztrakt metódusok felüldefiniálása Absztrakt osztály gyermeke lehet absztrakt, ha nem minden absztrakt metódust valósít meg 89/45 Az absztrakt osztály is használható referencia statikus típusaként
Absztrakt metódus és osztály Az absztrakt metódusok szerepe: rögzít egy tervezési döntést (szükséges metódusok halmaza), kényszeríti a leszármazott osztály(ok) programozóját az így meghatározott metódusok definiálására. Hibalehetőségek: törzs nélküli metódus, abstract minősítő nélkül, absztrakt metódust tartalmazó osztály abstract minősítő nélkül. 89/46
Teszt 6. Mely kulcsszavak szerepelnek egy visszatérési értékkel nem rendelkező, a leszármazott osztályban felülírható, védett példánymetódus fejében? static final void private abstract 89/47
Teszt 7. Mely kulcsszók szerepelhetnek egy osztálydefiníció fejrészében? public private abstract final static 89/48 extends
Teszt 8. Helyes vagy hibás az alábbi kód? public abstract class Komplex { int x, y, z; abstarct Komplex(int x, int y, int z){ this.x = x; this.y = y; this.z = z; 89/49
Interfész Technikailag: 89/50 Egy olyan osztályféle, amely csak absztrakt metódusokat és konstansokat tartalmazhat, tehát nem példányosítható. Nem örökléssel használjuk fel mint az absztrakt osztályokat, hanem implementálással. Implementálás: megvalósítjuk az interfészben deklarált metódusokat. Egy osztály implementálhat tetszőleges számú interfészt. Tervezésileg: Egy viselkedési leírás, amelyet egy osztály megvalósít. Lehetővé teszi, hogy ne egy konkrét megvalósításhoz (osztályhoz), hanem viselkedéshez (interfész) kössünk dolgokat.
Interfész Egy osztály interfészén a nyilvános elemeinek összességét értjük, azokat, amelyek az osztály megfelelő használatához szükségesek Az információ rejtés elve miatt az interfész általában csak metódusokból áll. Szintaktikája hasonló az osztályhoz, de a class kulcsszó helyett interface kulcsszót kell használni. Az interfészt tartalmazó fájl nevének meg kell egyeznie a kódban szereplő interfész névvel. interface Nyomtathato{ void nyomtat(); 89/51 class Konyv implements Nyomtathato{ String tartalom = "ABC"; public void nyomtat(){ System.out.println(tartalom);
Interfész Minden interfész automatikusan abstract, ezt módosítóval jelezni nem kell. Egy interfész kiterjeszthet más interfészeket (extends). Létezik többszörös interfész öröklés. Az interfész nem tartalmaz végrehajtható kódot, azt a megvalósító osztályban (implements) kell megadni. Konvenció, hogy az interfészek neve I -vel kezdődik. [módosító] interface Iazonosító extends If1, If2 { [elemek deklarációja] 89/52
Interfész Interfész implementálása: [módosító] class osztálynév implements If1, If2() { Szabályok: Ha egy osztály implementál egy interfészt, akkor köteles annak minden metódusát implementálni Az implementált elemeket nem módosíthatja Metódusok esetében a fejlécnek teljesen egyeznie kell Azonos nevű elemet az öröklődés során és az implementálás során nem kaphat meg 89/53
Interfész Tagok: 89/54 Csak konstansai vagy abstract tagjai lehetnek. Konstans: automatikusan public static final módosítóval rendelkeznek, de kiírhatók a módosítók. Metódusok: automatikusan public abstract módosítóval rendelkeznek, a public kiírható, az abstract nem ajánlott. Más módosítója nem lehet (pl. static, final, stb.). Interfész használata: Egy interfész új referencia típust vezet be -> mindenhol használható, ahol egy osztály. Változó deklarációban szerepelhet. Értékül minden olyan osztály objektumának felveheti a referenciáját, amely megvalósítja az interfészt!
Interfész Példák public interface A { public class B implements A { public class C implements A { public class D { A ref; ref = new B(); //OK! ref = new C(); //OK! ref = new D(); //NO! Hibás! ref = new A(); //NO! Hibás! A x = new B(); System.out.println(ref instanceof A); System.out.println(ref instanceof C); true true 89/55
Interfész Példák interface I1 { interface I2 extends I1 { class A implements I2 { class B implements I1 { I1 a = new A(); //OK! I2 b = new A(); //OK! a = b; //OK! Automatikus típuskonverzió b = (I2)a; //OK! Kikényszerített típuskonverzió A c = (A)a; //OK! Kikényszerített típuskonverzió a = new B(); //OK! b = (I2)a; //NO! Hibás! 89/56
Teszt 9. Jelölje meg az összes igaz állítást! Egy interfészben metódusfejeket és konstansokat definiálhatunk. Az interfész metódusai csak példánymetódusok lehetnek Egy interfésznek az osztályhoz hasonlóan csak egy interfész őse lehet. Az interfészekből példányok hozhatók létre, akárcsak az osztályokból. 89/57
Teszt 10. Az adott a Base interfész mellett melyik kód fordul le? interface Base { boolean m1 (); byte m2(short s); abstract class Class2 implements Base { public boolean m1() { return (7 > 4); interface Base2 implements Base { abstract class Class2 extends Base { public boolean m1() { return true; 89/58 class Class2 implements Base { boolean m1() { return false; byte m2(short s) { return 42;
Generikus típusok, metódusok 89/59
Generikus típusok, metódusok Általános jellegű, nem konkrét típust használó típusok, metódusok definiálása. Tipikus példa: adattároló osztály Régebbi megoldás: Object referencia használata. public class Tarolo { private Object object; public void set(object object) { this.object = object; public Object get() { return object; 89/60
Generikus típusok, metódusok A Tarolo osztályt egészek tárolására használjuk 89/61 Véletlenül belekerül egy String Az eredeti típusos objektum visszakapásához castolni kell A program lefordul, a hiba csak futtatáskor derül ki! public class TaroloPrg { public static void main(string args[]) { Tarolo t = new Tarolo(); t.set(3); int i = (Integer)t.get(); t.set("retek"); int k = (Integer)t.get(); c:\java8>java TaroloPrg Exception in thread "main" java.lang.classcastexception: java.lang.string cannot be cast to java.lang.integer at TaroloPrg.main(TaroloPrg.java:7)
Generikus típusok, metódusok Megoldás: generikus "típussal paraméterezett típus" használata public class GenericTarolo<T> { private T t; public void set(t t) { this.t = t; public T get() { return t; Konvenció: Paraméterként használt típusok jelölése: E - Elemek K - Kulcs N - Szám T - Típus V - Érték 89/62
Generikus típusok, metódusok A GenericTarolo osztályt egészek tárolására használjuk Az adat közvetlenül kinyerhető, nincs cast! Véletlenül belekerül egy String, a hiba már fordításkor jelentkezik! public class GTPrg { public static void main(string args[]) { GenericTarolo<Integer> gt = new GenericTarolo<Integer>(); gt.set(3); int i = gt.get(); gt.set("retek"); c:\java8>javac -d. GTPrg.java GTPrg.java:5: set(java.lang.integer) in GenericTarolo<java.lang.Integer> cannot be applied to (java.lang.string) gt.set("retek"); ^ 89/63
Generikus típusok, metódusok public class GenArray { 89/64 public static <E> void printarray(e[] inputarray) { for(e elem : inputarray) System.out.printf("%s ", elem); System.out.println(); //üres elválasztó sor kiírása public static void main(string args[]) { Integer[] intarray = { 1, 2, 3, 4, 5 ; Double[] doublearray = { 1.1, 2.2, 3.3, 4.4 ; Character[] chararray = { 'H', 'E', 'L', 'L', 'O' ; System.out.println("Az egész típusú tömb tartalma:"); printarray(intarray); System.out.println("\nA valós típusú tömb tartalma:"); printarray(doublearray); System.out.println("\nA karakter típusú tömb tartalma:"); printarray(chararray);
Hibakezelés 89/65
Hiba, biztonságos program Szintaktikai hiba 89/66 imt i = 0; system.out.println('alma'); for (int i = 0, i<= 10, i++)... Szemantikai hiba int a=0, b=10/a; x1=(-b+math.sqrt(b*b-4*a*c))/2*a; args[++i] Biztonságos program A szemantikai hibákat megpróbálja lekezelni a belőlük következő hibákkal együtt Ennek módja: kivételkezelés
Kivételkezelés Fogalma: A hibakezelés egy formája, amelyben elválasztható a hibalekezelő kódrészlet a normál működés kódjától, valamint a jelezhetjük a kódunkban előforduló hibákat és előírhatjuk kezelésüket a kódunkat használó kód számára. Kivételek keletkezése Kivétel létrejöhet egy végrehajtás során előforduló hiba hatására (I/O hiba, tömbindex túllépés, stb.), valamint saját magunk is létrehozhatunk kivételeket. A kivétel létrejöttét a kivétel megdobódásának nevezzük. A kivétel elkapását a kivétel lekezelésének nevezzük. 89/67
Kivételkezelés Kivételek: Javaban a kivételek objektumok. A kivétel osztályok az Exception osztály leszármazottai Fajtái: 89/68 Kötelezően lekezelendő kivételek: a fordító ellenőrzi, hogy ezeket lekezeljük-e. Például az I/O műveletek során előforduló kivételek. Nem kötelezően lekezelendő kivételek: nem kötelező lekezelni. Ezek a RuntimeException leszármazottai, ami viszont az Exception leszármazottja. Például a null referencia hiba, tömb index túllépés, stb. A saját kivételosztályokat ez alapján vagy az Exception osztályból származtatjuk (közvetve, közvetlenül) (ekkor kötelező lekezelni) vagy a RuntimeException-ból (ekkor nem kötelező lekezelni).
Kivételosztályok néhány fontosabb Ellenőrzött kivételek IOException Ki- és bemeneti hibák esetén váltódik ki, pl. konzolról való beolvasáskor. FileNotFoundException EOFException Nem ellenőrzött kivételek Az IOException egyik alkivételosztálya. Akkor kapjuk például, ha nem létező fájlba próbálunk írni. Fájl végét jelző kivétel. Szintén az IOException alosztálya. RuntimeException NullPointerException ArithmeticException Valamennyi futásidejű kivétel ősosztálya. Olyan esetben kapjuk, ha null értékű, azaz inicializálatlan változóra vagy objektumra hivatkozunk. Túlindexelést jelző kivétel. Akkor kapjuk, ha egy tömb, karakterlánc, vagy más indexelhető szerkezet nem létező indexére hivatkozunk. Aritmetikai műveletek hibájakor kapjuk, például nullával való osztáskor. NumberFormatException Akkor kapjuk például, ha nem megfelelő számot tartalmazó karakterláncot próbálunk szám típusúvá konvertálni. IndexOutOfBoundsException IllegalArgumentException 89/69 Akkor váltódik ki, ha egy metódust nem megfelelő paraméterekkel hívunk.
Kivételkezelés A védett kódot try blokkban helyezzük el: try { utasítások Az első kivételt kiváltó utasítással befejeződik a try blokk végrehajtása, létrejön egy kivétel objektum. Fontos: a try blokk kivételt kiváltó utasítása utáni utasítások tehát mindig kimaradnak! try { X kód, ami kiválthat egy kivételt további utasítások catch ( Kivétel típusa Azonosító ) { 89/70
Kivételkezelés A kivétel lekezelését szolgáló utasítások catch blok(kok)ban helyezkednek el. catch (típus, azonosító) { utasítások A catch minden esetben a try blokkot követi, nem lehet köztük más utasítás. Egy try blokkhoz tartozhat több catch is. Mindig a szűkebb kivételt követi a bővebb kivétel lekezelése. catch ( Kivétel típusa Azonosító ) { kód, ami a kivétel kiváltódásakor lefut catch ( Kivétel típusa Azonosító ) { 89/71 kód, ami a kivétel kiváltódásakor lefut
Kivételkezelés finally: A cacth(ek) után szerepelhet. Az utolsó catch blokk és a finally blokk között nem lehet más utasítás. Minden esetben lefut: Ha kivétel keletkezett a try blokkban, egy catch blokk végrehajtása után. Ha nem volt kivétel, a try blokk utolsó utasítása után. try { X kód, ami kiválthat egy kivételt catch ( Kivétel típusa Azonosító ) { 89/72 finally { kód, ami a kivétel kiváltódásakor lefut kód, ami mindig lefut
Kód belépési pont try { Kód végrehajtás kivétel esetén Kód végrehajtás hibamentes esetben X kód, ami kiválthat egy kivételt catch ( Kivétel típusa Azonosító ) { kód, ami a kivétel kiváltódásakor lefut finally { 89/73 Kód kilépési pont kód, ami mindig lefut
Kivételkezelés A Java kivételkezelése nyitott, ami azt jelenti, hogy bárki létrehozhat saját névvel és funkcionalitással ellátott kivételosztályokat, amelyeket célszerű az Exception osztályból származtatni. Az új kivételosztályok nevében célszerű az "Exception" szót is szerepeltetni, hogy utaljon annak szerepére. Kivétel dobása (throw utasítás) throw kivételobjektumref; pl: throw new IOException("Hiba van"); throw sajátkivételosztálynév("saját hibaüzenet"); 89/74
Kitérő verem adatszerkezet Verem: Verem esetén azt az elemet vehetjük ki legelőször, amelyiket utoljára tettük be. Működési módja miatt LIFO (last in first out) adatszerkezetnek nevezik. Műveletek: berakás, kivétel C, B, A A, B, C 3. 2. 1. 3. 2. 1. Berak C B Kivesz 89/75 A
Kivételkezelés Példa Készítsünk egy komplett verem kezelő alkalmazást Ha a verembe nem lehet betenni, vagy nem lehet belőle kivenni adatot, használjunk hibakezelést A hibakezeléshez írjunk saját hiba osztályt public class Verem_Exception extends Exception { public Verem_Exception(String hibauzenet){ super(hibauzenet); 89/76
Kivételkezelés Példa public class Verem{ private final static int MERET = 3; private int[] verem = new int[meret]; private int mutato = 0; 89/77 public void betesz(int i) throws Verem_Exception { if (mutato < MERET) { verem[mutato] = i; System.out.println("A szám: ("+i+") a verembe helyezve!"); mutato++; else throw new Verem_Exception("A verem megtelt!"); public int kivesz() throws Verem_Exception { if (mutato == 0) throw new Verem_Exception("A verem üres!"); mutato--; int i = verem[mutato]; System.out.println("A szám: ("+i+") a veremből kivéve!"); return i;
public class VeremPrg{ public static void main(string args[]) { Verem v = new Verem(); try { v.betesz(21); v.betesz(52); v.betesz(77); v.betesz(99); catch (Verem_Exception ve) { System.err.println(ve); 89/78 System.out.println(); try { v.kivesz(); v.kivesz(); v.kivesz(); v.kivesz(); catch (Verem_Exception ve) { System.err.println(ve);
Kivételkezelés Kivétel terjedése A kivétel nem képes tovább terjedni egy metódusblokkból, hacsak a metódus fejlécében nincs megadva a kivétel típusa vagy annak őse. Ha egy metódus fejlécében kötelezően lekezelendő kivételtípus van megadva, akkor a hívását kötelező olyan try utasításba ágyazni (közvetlenül vagy közvetve), amelyik lekezeli a kivételtípust. Mivel a catch blokkok keresése sorban történik mindig előbbre kell írni azt a catch blokkot, amelyik egy leszármazott típust kap el és csak utána azt, amelyik az őst. A utolsó catch blokként lehet írni egy Exception-t elkapót, hiszen az minden kivételt képes elkapni. 89/79
Teszt 11. Helyes vagy hibás az alábbi kód? public class Teszt { public static void main(string args[ ]) { int x = new int(5); Helyes Hibás Miért? 89/80
Teszt 12. Mit ír ki a kód? 89/81 public class SuperClass { int i, j; SuperClass(int i, int j) { this.i = i; this.j = j; SuperClass() { this(2, 3); public class Program extends SuperClass { public Program() { System.out.println(i + j); 0 5 null Szintaktikailag hibás, így nem fut le a kód. public static void main(string[ ] args) { new Program();
Teszt 13. Jelölje meg az összes igaz állítást! 89/82 Egy konstruktorból meghívható az osztály egy másik konstruktora. Egy objektum létrehozásakor az öröklési ág minden osztályának lefut legalább egy konstruktora. Ha a konstruktor első sorában nem szerepel this() vagy super() hívás, akkor futási hiba keletkezhet Az osztályadatokat csak a konstruktorban lehet inicializálni. A konstruktor a leszármaztatott osztályban felüldefiniálható
Teszt 14. Milyen típusú kivételt dob a parseint() metódus, ha illegális adatot kap? NumberFormatError NumberException NumberFormatException ArithmetricException 89/83
Teszt 15. Helyes vagy hibás az alábbi kód? public class Fut { public static void main(string[] args) { try { finally { System.out.println("BB"); Helyes Hibás 89/84
Teszt 16. Helyes vagy hibás az alábbi kód? public class Fut { public static void main(string[] args) { try { System.out.println("BB"); catch (Exception e) { System.out.println(e.getMessage()); catch (ArithmeticException a) { System.out.println(a.getMessage()); 89/85 Helyes Hibás
89/86 Teszt 17. Az alábbi kód egy A.java nevű fájlban van. Mi történik, ha megpróbáljuk lefordítani? public class A { public void m1() {System.out.print("A.m1,"); protected void m2() {System.out.print("A.m2,"); private void m3() {System.out.print("A.m3,"); void m4() {System.out.print("A.m4,"); class B { public static void main(string[] args) { A a = new A(); a.m1(); a.m2(); a.m3(); a.m4(); Lefordul, minden OK. Nem fordul, mert egy fájlban csak egy osztály lehet Nem fordul, mert hiba van az A osztályban Nem fordul, mert hiba van a B osztályban
89/87 Teszt 18. Az alábbi kód egy A.java nevű fájlban van. Lefordítjuk. Hogyan indítjuk el a programot? public class A { public void m1() {System.out.print("A.m1,"); protected void m2() {System.out.print("A.m2,"); public void m3() {System.out.print("A.m3,"); void m4() {System.out.print("A.m4,"); class B { public static void main(string[] args) { A a = new A(); a.m1(); a.m2(); Sehogy, csak az A osztály fordul le. a.m3(); java A a.m4(); java A.B java B
Felhasznált irodalom Ficsor Lajos elektronikus jegyzetei Elek Tibor elektronikus jegyzetei Fenyvesi Tibor, Szabó László elektronikus anyagai www.tutorialspoint.com/java oldal anyagai prog.hu oldal anyagai Nyékiné G. Judit (szerk.): JAVA 2 útikalauz programozóknak, ELTE TTK Hallgatói Alapítvány, Budapest, 1999 Daniel J. Berg, J. Steven Fritzinger: JAVA felsőfokon,wiley, 1999 Joshua Bloch: Hatékony Java, 2008 89/88
VÉGE VÉGE 89/89