OOP Objektov orientovan programovn Programovat lze klasicky ili
OOP - Objektově orientované programování Programovat lze klasicky čili neobjektově - tedy s využitím pouze primitivních typů a statických členů tříd. Avšak objektová metodika, zahrnující i objektově orientovanou analýzu (OOA), umožňuje vytvářet snadněji složité softwarové systémy. Objektová metodika zavedená v Javě má tyto důležité rysy: • • • Zavedení jasného pořádku. Abstrakci (abstraction), tj. zobecnění pohledu a částečná řešení. Dědičnost (inheritance), tj. získávání vlastností a schopností děděním. Zapouzdření (encapsulation), tj. skrývání části tříd a interfejsů. Polymorfismus (polymorphism), tj. obecný přístup k mnohotvarosti. PJV 02 1
Pořádek Třídy (class) a interfejsy (interface, rozhraní) jsou v Javě jedinými základními stavebními kameny. Pro pořádek jsou ukládány do tzv. balíčků (package). • Základním balíčkem je java. lang, bez kterého nemůže Java fungovat. • Zdrojové soubory, tj. kompilační jednotky, musejí mít extenzi jména. java a obsahují: - Jeden příkaz package, který je vždy první. - Několik (i žádný) příkazů import ( import java. lang. *; je defaultní ). - Definice tříd či interfejsů v libovolném pořadí. - Dokumentační komentáře před třídou, interfejsem a jejich členy. - Ostatní komentáře (řádkové nebo blokové) kdekoli. PJV 02 2
Kompilační jednotka C. java package aa. bb. cc; import xx. *; import xx. yy. *; abstract class B { … } public class C { … } PJV 02 aa bb cc A. class B. class A { … } C. class interface P { … } interface Q { … } interface R { … } P. class Q. class R. class 3
Pořádek • Ve zdrojovém souboru může být nanejvýš jedna veřejná třída (tj. označená modifikátorem public) a v tom případě musí být jméno zdrojového souboru shodné se jménem té třídy. Při kompilaci vznikne právě tolik souborů bytekódu, kolik je ve zdrojovém tříd a interfejsů ( i vnitřních). Vzniklé soubory mají jména podle těch tříd či interfejsů - avšak s extenzí. class. ( Vnitřní třídy či interfejsy mají vlastní jména složená ze sekvence jmen vnějších tříd či interfejsů oddělených dolarem. ) • Aby byly bytekódové soubory přístupné, musejí být uloženy v adresářích a to tak, aby cesta odpovídala operandu v příkazu package ( tzv. classpath, musí odkazovat na adresář v němž se nalézá adresář shodného jména s první složkou jména uvedeného v příkazu package. ) PJV 02 4
Jména • Všechny položky programu musejí být pochopitelně rozlišitelné různými jmény. V některých případech se rozlišují shodná jména dle principu "košile-kabát". • Třídy a interfejsy jsou rozlišitelné pomocí úplných jmen. Úplné jméno třídy či interfejsu tvoří prefix zadaný v příkazu package a vlastní jméno uvedené v definici. • Atributy přejímají úplná jména tříd či interfejsů jako prefix vlastních jmen. • Metody a konstruktory přejímají úplná jména tříd či interfejsů jako prefix vlastních signatur. Signatura metod či konstruktorů se skládá z jejich vlastních jmen a sufixu, který odpovídá sekvenci typů jejich parametrů. • Metody či konstruktory v téže třídě mohou mít shodná vlastní jména, avšak v tom případě musejí mít různé signatury. Takové metody se označují jako přetížené (overload). Jsou výhodné tehdy, mají-li obdobnou funkcionalitu. PJV 02 5
Abstrakce a konkretizace • Abstrakce (abstraction) je velmi důležitým pohledem na svět - i ten virtuální. • Abstrakce je základem taxonomie složitých systémů, přičemž na jedné straně umožňuje záměrně zanedbávat nedůležité aspekty a na druhé straně je přidávat a tak přecházet od abstraktního ke konkrétnímu. • Díky abstrakci můžeme vytvářet neúplné popisy a plány, které však i tak mohou být velmi užitečné. Jednak vyjasňují strukturu systému a jednak po pozdějším doplnění se stanou úplnými a tedy konkrétními. • Jen podle konkrétních plánů (tříd) lze vytvářet výrobky (objekty). • Konkrétní třída k může zabránit konstrukci objektů tím, že pomocí modifikátoru private utají všechny své konstruktory a nepřipustí jejich použití ani jiným způsobem. PJV 02 6
Abstraktní třídy, interfejsy a metody • Abstraktní třída je označena modifikátorem abstract ( konkrétní třída nikoli). Má také alespoň jeden konstruktor, avšak nelze podle ní objekty vytvářet. Abstraktní třída nemůže být finální. • Statické atributy, metody a inicializátory abstraktní třídy jsou funkční. • Abstraktní metoda je označena modifikátorem abstract a místo těla má jen středník. • Obsahuje-li třída abstraktní metodu ( vlastní definovanou či zděděnou a nepřepsanou na konkrétní ), pak musí být označena jako abstraktní. • Interfejs lze považovat za omezenou abstraktní třídu, která má všechny metody abstraktní. Interfejs umožňuje jednak částečný pohled na třídu a jednak slouží s výhodou jako norma či požadavek. PJV 02 7
Dědičnost Object U E F P A A D Q R S B B T T G třída PJV 02 C C abstr. třída interfejs extends implements 8
Dědičnost (inheritance) • Dědičnost velmi usnadňuje: - Přístup k existujícím třídám a interfejsům a jejich pochopení. - Vytváření nových tříd a interfejsů s využitím vlastností a schopností již existujících, ověřených a dokumentovaných. • Všechny třídy ( bez ohledu v jakém balíčku jsou uloženy ) tvoří jediný, dědičný, kořenový strom orientovaný směrem ke kořeni, kterým je třída java. lang. Object. Tedy každá třída, kromě java. lang. Object, má právě jednoho přímého předka (superclass = nadtřída), jehož je přímým potomkem (subclass = podtřída). Svého přímého předka vyznačuje potomek ve své hlavičce za klíčovým slovem extends. Neuvede-li to, je přímým předkem java. lang. Object. • Třída java. lang. Object nemá žádného předka ( je to jakýsi prapředek Adam ) a tedy nic nezdědila. Všechny ostatní třídy jsou tedy jejími přímými anebo nepřímými potomky. • Třída může také dědit od interfejsů uvedených v seznamu za klíčovým slovem implements. PJV 02 9
Syntaxe tříd a interfejsů • class Jméno. Třídy [extends Jméno. Nad. Třídy ] [implements Jméno. Interfejsu [, Jméno. Interfejsu ]. . . ] {. . // atributy. . // konstruktory. . // metody. . // další složky } • interface Jméno. Interfejsu [extends Jméno. Interfejsu [, Jméno. Interfejsu ]. . . ] {. . // atributy. . // metody. . // další složky } PJV 02 10
Obsah tříd • Třídy slouží jako: - Úložiště statických položek tříd. - Plán pro vytváření objektů obsahující nestatické položky. - Zdroj neprivátních členů pro dědictví potomkům. - Privátní zdroj funkcionality potomků. • Ve třídách lze definovat téměř v libovolném pořadí (dodržuje raději toto): - Statické atributy. - Statické inicializátory (vhodné jen pro složitější inicializace). - Nestatické atributy. - Statické metody. - Nestatické inicializátory (neužívají se). - Konstruktory. - Nestatické metody. - Vnitřní interfejsy (jen pro složité případy). - Vnitřní třídy (jen pro složité případy). PJV 02 11
Obsah interfejsů • V interfejsech lze definovat v libovolném pořadí tyto členy: - statické finální inicializované atributy, - abstraktní nestatické metody, - vnitřní interfejsy (vhodné jen pro složité případy), - vnitřní třídy (vhodné jen pro složité případy). • Všechny členy jsou povinně veřejné (modifikátor public netřeba uvádět). Všechny atributy jsou povinně statické a finální (tedy modifikátory static final netřeba uvádět) a proto musejí být inicializovány přímo v definici. • Všechny metody jsou povinně abstraktní (tedy modifikátor netřeba uvádět). • Při vícečetném dědění může dojít ke kolizi shodných jmen děděných členů. • Avšak u metod shodných signatur může ke kolizi dojít jen neshodou jejich návratových typů. PJV 02 12
Dědictví tříd • Třída dědí všechny neprivátní členy: - z jedné třídy (uvedené za klíčovým slovem extends), - z interfejsů (ze seznamu za klíčovým slovem implements). Dědictví nemůže potomek odmítnout a tedy potomek není nikdy chudší než předek - zpravidla však mnohem bohatší. • V nově vytvářené třídě lze: - definovat nové členy, - přepsat zděděné metody novými metodami (override), - zastínit zděděné atributy novými atributy (shadow), - definovat nové konstruktory a inicializátory. • Zdědění abstraktních metod, není " výhodné dědictví ", ale břemeno, se kterým se může třída vyrovnat jedním z těchto způsobů: - přepsat všechny zděděné abstraktní metody na konkrétní, - označit je modifikátorem abstract a bude tudíž abstraktní třídou. PJV 02 13
Dědictví interfejsu • Interfejs může dědit jen od interfejsů. • Při dědění metod nemůže dojít ke kolizi, neboť všechny metody jsou abstraktní a nemají tělo. • Při dědění stejnojmenných atributů od více předků-interfejsů k víceznačnosti může dojít, což Java ohlásí a to i když jsou inicializovány na stejnou hodnotu. PJV 02 14
Zapouzdření (encapsulation) • Zapouzdření umožňuje skrýt atributy, metody, konstruktory, vnitřní třídy a vnitřní interfejsy, což je velmi praktické, neboť to zaručuje spořádanost a bezpečnost při zacházení s třídami a objekty. Inicializátory jsou skryté již tím, nemají jméno. • V interfejsech lze zapouzdřit pouze vnitřní třídy a vnitřní interfejsy. • Zapouzdřování se realizuje prostřednictvím modifikátorů viditelnosti, kterými označujeme členy a konstruktory. modifikátor viditelnost public - odevšad protected - ve vlastním balíčku a v potomcích kdekoli v balíčcích (default) - ve vlastním balíčku (modifikátor se nepíše) private - jen ve vlastní třídě • K privátním členům nějaké třídy lze přistupovat z jiných tříd jen pomocí neprivátních metod oné třídy. PJV 02 15
Getry a setry • Je zavedená dobrovolná jmenná konvence pro přístupové metody, které hodnotu atributu vydávají (tzv. getry) anebo nastavují (tzv. setry). Např. : private int cena. Kusu; // atribut public int get. Cena. Kusu( ) {return cena. Kusu; } // getter public void set. Cena. Kusu (int cena. Kusu) {. . this. cena. Kusu = cena. Kusu; } // setter private Point vrchol. Hory; public Point get. Vrchol. Hory () { return vrchol. Hory ; } public void set. Vrchol. Hory (Point vrchol. Hory) {. . this. vrchol. Hory = vrchol. Hory ; } // atribut // getter // setter PJV 02 16
Zastínění a přepsání • Zastínění atributů Nevyhovuje-li potomkovi hodnota atributu zděděná od předka, lze ji v potomkovi přepsat vložením atributu stejného jména, přičemž na typech a modifikátorech nezáleží. • Přepsání metod ve třídě Nevyhovuje-li potomkovi činnost metody zděděné od předka, lze ji v potomkovi přepsat vložením metody, která: - má shodnou signaturu, - má shodný návratový typ, - nerozšiřuje množinu vyhazovaných výjimek (za klíčovým slovem throws), - není finální, tj. nemá modifikátor final. - přepsáním metody v potomkovi metoda v předkovi nezmizí. Je z potomka dostupná pomocí klíčového slova super. PJV 02 17
Tvorba objektů • Objekty lze vytvářet, aktivovat a operovat nad nimi pouze v run-time. • Lze je ukládat na vnější paměti, přenášet a využívat telekomunikačně. • Objekt (čili instance třídy) se vytváří v operační paměti na tzv. haldě. Obsahuje nestatické čili instanční atributy primitivních i referenčních typů. Objekt nemusí být paměťově kontinuální. Dále obsahuje referenci k vlastní třídě, kde jsou uloženy metody. Kódy metod jsou reentrantní (neobsahují proměnná data) a existují pouze v jednom exempláři. • JVM vede evidenci vytvořených objektů a referencí, které na ně odkazují. • Rušení již nereferovaných objektů provádí občas garbage collector. Ten lze zavolat pomocí System. gc(); • Pro vytvoření objektu je nutný tzv. konstruktor. PJV 02 18
Konstruktory • Konstruktor připomíná konkrétní nestatickou metodu (funkci) - avšak: – Má jméno stejné jako třída. – Nemá vyznačený návratový typ, tj. ani void. To není třeba, neboť je zřejmé, že vrací referenci příslušného typu na objekt, který vytvořil. – Není členem třídy, nedědí se a nemůže být abstraktní. • Každá třída má alespoň jeden konstruktor - mohou být přetížené. • Konstruktory mají také modifikátory viditelnosti - mohou být i privátní. • Není-li ve třídě žádný konstruktor definován, pak je k dispozici tzv. implicitní konstruktor, který nemá žádné parametry a volá pouze konstruktor předka bez parametrů: public Jméno. Třídy ( ) { super( ) ; } Je-li však nějaký konstruktor definován, pak implicitní již není k dispozici a eventuální konstruktor bez parametrů je nutno definovat explicitně – případně i s neprázdným tělem. PJV 02 19
Vzájemné volání konstruktorů Pro začátek těl konstruktorů platí tato pravidla pro tři případy: • public Jméno. Třídy ( ) { super(…); … } Zprvu se volá příslušný konstruktor přímého předka. • public Jméno. Třídy ( ) { this(…); …} Zprvu se volá příslušný konstruktor z téže třídy. Tuto fintu lze použít i několikrát, avšak nesmí dojít k cyklu. • public Jméno. Třídy ( ) { … } Pokud tělo začíná jinak než v předchozích případech, pak se skrytě na začátek přidá volání konstruktoru předka bez parametrů. Neexistuje-li onen, dojde k chybě. Tedy tato forma je ekvivalentní této: public Jméno. Třídy ( ) { super(); … } PJV 02 20
Vytvoření objektu Objekt lze vytvořit instanciací třídy jedním z těchto způsobů: • aktivací konstruktoru pomocí klíčového slova: Jméno. Třídy x = new Jméno. Třídy(…); • formou super(. . . ); , v prvním příkazu v konstruktoru. • nestatickou metodou new. Instance() z třídy java. lang. Class např. takto: Class c = Class. for. Name("ÚplnéJméno. Třídy"); Object x = c. new. Instance(); ÚplnéJméno. Třídy z = (ÚplnéJméno. Třídy) x; Vytvoření objektu se nemusí povést, když konstruktor nebo inicializátor vyhodí výjimku. PJV 02 21
Inicializace atributů • Statické (čili třídní) i nestatické (čili instanční) atributy lze inicializovat přímo v jejich definici - např. : static int i = 3*5, j = Integer. parse("333", 4) ; Rectangle r = new Rectangle(new Point(i+1, j+2), new Dimension(100, 50) ); Ovšem nutno nejdřív definovat, pak použít. • Inicializace statických atributů se provede jen jednou a to při zavedení třídy či interfejsu do paměti v run-time. Inicializace nestatických atributů se provede při konstrukci každého objektu. • Ve třídách lze také definovat inicializátory statické i nestatické. • Inicializátory připomínají velmi degenerované metody. Nic nevracejí, nemají signaturu tj. jméno a parametry a nevyznačují vyhazování výjimek (throws). Nelze je ani programově volat a jelikož nejsou členy třídy - nedědí se. • Statické inicializátory slouží ke složitější inicializaci statických atributů. Syntakticky jsou velmi prosté: static {. . . }. • Je-li jich více, provádějí se v run-time jen jednou v pořadí zápisu. PJV 02 22
Inicializátory • Statické inicializátory – příklad: static int[ ] a = new int[256] ; static { for ( int i = 0; i < a. length; i++ ) a[i] = i; } static { for ( int i = 0; i < a. length; i++ ) a[i] = a[i]+1; } • Nestatické inicializátory slouží k inicializaci atributů. Jsou pouhým blokem: {. . . }. Je-li jich více a provádějí se v run-time po dokončení konstrukce přímého předka a inicializace nestatických atributů a to v pořadí v zápisu. Např. : int[ ] a = new int[256] ; { for ( int i = 0; i < a. length; i++ ) a[i] = i; } Konstrukce objektu je tím složitější čím je třída vzdálenější od kořene a čím je dědictví větší. Nejprve se zkonstruuje Object a pak všechny objekty po linii k danému objektu. Bývá nutná četná alokace paměti. Např. : int i = 100, j = i+10, w = 300, h = w/2 ; Rectangle r = new Rectangle( new Point( i, j ), new Dimension(w , h ) ); PJV 02 23
- Slides: 23