Objektově orientované programování (zkracováno na OOP) je metodika vývoje softwaru, založená na následujících myšlenkách, koncepci:
- Objekty – jednotlivé prvky modelované reality (jak data, tak související funkčnost) jsou v programu seskupeny do entit, nazývaných objekty. Objekty si pamatují svůj stav a navenek poskytují operace (přístupné jako metody pro volání).
- Abstrakce – programátor, potažmo program, který vytváří, může abstrahovat od některých detailů práce jednotlivých objektů. Každý objekt pracuje jako černá skříňka, která dokáže provádět určené činnosti a komunikovat s okolím, aniž by vyžadovala znalost způsobu, kterým vnitřně pracuje.
- Zapouzdření – zaručuje, že objekt nemůže přímo přistupovat k „vnitřnostem“ jiných objektů, což by mohlo vést k nekonzistenci. Každý objekt navenek zpřístupňuje rozhraní, pomocí kterého (a nijak jinak) se s objektem pracuje.
- Skládání – Objekt může obsahovat jiné objekty.
- Delegování – Objekt může využívat služeb jiných objektů tak, že je požádá o provedení operace.
- Dědičnost – objekty jsou organizovány stromovým způsobem, kdy objekty nějakého druhu mohou dědit z jiného druhu objektů, čímž přebírají jejich schopnosti, ke kterým pouze přidávají svoje vlastní rozšíření. Tato myšlenka se obvykle implementuje pomocí rozdělení objektů do tříd, přičemž každý objekt je instancí nějaké třídy. Každá třída pak může dědit od jiné třídy (v některých programovacích jazycích i z několika jiných tříd).
- Polymorfismus – odkazovaný objekt se chová podle toho, jaké třídy je instancí. Pokud několik objektů poskytuje stejné rozhraní, pracuje se s nimi stejným způsobem, ale jejich konkrétní chování se liší podle implementace. U polymorfismu podmíněného dědičností to znamená, že na místo, kde je očekávána instance nějaké třídy, můžeme dosadit i instanci libovolné její podtřídy, neboť rozhraní třídy je podmnožinou rozhraní podtřídy. U polymorfismu nepodmíněného dědičností je dostačující, jestliže se rozhraní (nebo jejich požadované části) u různých tříd shodují, pak jsou vzájemně polymorfní.
Objektový přístup programování trochu jinak:
- Modelování problému jako systému spolupracujících tříd
- Třída modeluje jeden koncept
- Třídy umožní generování instancí, objektů příslušné třídy
- Jednotlivé objekty spolu spolupracují, „posílají si zprávy“,
- Třída je „vzorem“ pro strukturu a vlastnosti generovaných objektů
- Každý objekt je charakteristický specifickými hodnotami svých atributů a společnými vlastnostmi třídy
Třída je základem uživatelského programu v jazyku Java (nebo obecně v OOP). Třída = koncept. Každá třída je reprezentována svými prvky, objekty dané třídy. Každá třída je charakterizována svými vlastnostmi, svými funkčními možnostmi a svými parametry. Třída tedy popise nějaký “objekt” reálného problému a udržuje jeho parametry a poskytuje metody pro práci s ním. Třída v bodech:
- jsou deklarovány další (static) funkce (či procedury) třídy, případně i v jiných třídách: projekt, package
- mohou být deklarovány statické proměnné, které jsou použitelné jako nelokální proměnné ve funkcích dané třídy
- program probíhá spuštěním příkazů metody main
Třída je definována množinou identifikátorů, které mají třídou definovaný význam: – data: proměnné, konstanty (členské proměnné, datové složky, atributy) – metody: pracovně funkce a procedury
Třída jako šablona pro generování konkrétních instancí třídy, tzv. objektů. Jednotlivé instance třídy (objekty) mají stejné metody, ale nacházejí se v různých stavech – Stav objektu je určen hodnotami instančních, členských proměnných. Schopnosti objektu jsou dány instančními metodami třídy. V jazyku Java lze objekty (instance tříd) vytvářet pouze dynamicky pomocí operátoru new a přistupovat k nim pomocí referenčních proměnných (podobně jako u pole). Třída bez vytvořené instance (objektu) může „pracovat“ pouze „staticky“ (mohou být použity jen její statické metody či proměnné – procedurální přístup). Statické proměnné = společné pro všechny instance třídy.
Příkladem třídy jako datového typu je třída Complex
- hodnotami typu jsou komplexní čísla tvořená dvojicemi čísel typu double (reálná a imaginární část)
- množinu operací tvoří obvyklé operace nad komplexními čísly (absolutní hodnota, sčítání, odčítání, násobení a dělení)
Třída může definovat dva druhy metod:
- statické metody – metody třídy, procedury a funkce
- instanční metody – metody objektů
- Statickým metodám třídy budeme i nadále říkat procedury a funkce
- Instančním metodám budeme zkráceně říkat metody
Každý objekt má implicitní operátor this, který obsahuje odkaz na "svou" instanci
- Hodnotou operátoru this je odkaz na objekt pro který byla metoda zavolána (implicitní parametr odkazu na objekt)
- Umožňuje přístup k vlastním instančním proměnným v instančních metodách
- Používá se v přetížených konstruktorech
Podobně funguje operátor this i pro metody:
- pokud je instanční metoda volána z jiné instanční metody té samé třídy, potom se volá pomocí operátoru this (operátor se může vynechat)
- pokud se volá metoda z jiného kontextu, uvádí se před jejím jménem přístup k příslušné instanci (tečka notace) např. předané parametrem
- nelze volat ze statické metody - u ní by nebylo jasné, kam this odkazuje
Objekt - datový prvek, instance třídy, dynamicky vytvořen podle „vzoru“- třídy (viz sekce Třída) Složky objektu:
- atributy objektu (datové složky, členské proměnné (member variables)) - proměnné objektu, definují typ a jména vlastností objektu
- metody
Zapouzdření v objektech znamená, že k obsahu objektu se nedostane nikdo jiný, než sám vlastník. Navenek se objekt projeví jen svým rozhraním (operacemi, metodami) a komunikačním protokolem. (Př. private proměnné -> pro manipulaci metody: nejjednodušší např. settery, gettery). Důležité pojmy:
- Skládání objektů: udržování odkazů na jiné objekty (objekt obsahuje jiné objekty).
- Delegování: Využívání služeb jiných objektů.
Zapoudření = Daný stav objektu je přístupný nebo měnitelný pouze prostřednictvím rozhraní poskytovaného objektem. Důsledkem zapouzdření je autorizovaný přístup k datům, při kterém zajistíme, že s daty objektu nebude možné z vnějšku třídy manipulovat jinak než pomocí metod této třídy, které tvoří komunikační rozhraní třídy. Zapouzdření je zajištěno pomocí modifikátorů:
- public - lze je volat odkudkoli
- protected** - lze volat ze stejného package nebo odovozené třídy
- neurčený = package private - lze volat ze stejného package; nelze volat z odvozené třídy ležící v jiném package
- private - lze je volat pouze z metod téže třídy
Dědičnost je mechanismus umožňující
- rozšiřovat datové položky tříd (také modifikovat)
- rozšiřovat či modifikovat metody tříd
Dědičnost umožní
- vytvářet hierarchie tříd
- „předávat“ datové položky a metody k rozšíření a úpravě
- specializovat, „upřesňovat“ třídy
Dvě základní výhody dědění
- Dědění má praktický význam v znuvupoužitelnosti programového kódu
- Dědičnost je základem polymorfismu
Java dědí pouze od jediného předka. Mnohonásobné dědění se řeší pomocí rozhraní. Nejvyšší třída je Object, všechny třídy dědí třídu Object (metody: equals (standardní implementace neporovnává objekty, ale reference), toString, hashCode (stejné objekty by měly generovat stejný hashCode), clone..)
Obsahuje-li deklarace třídy členské proměnné objektového typu, pak mluvíme o kompozici objektů. Kompozice vytváří hierarchii objektů, nikoli však dědičnost. Například třída, která obsahuje jako členskou proměnnou integer, ale hlavně i například Datum (další, jiný objekt). (Kompozice označována jako struktura HAS (“má”, dědičnost jako ISE “je”). Kompozice objektů je tvořena atributy objektového typu, pouze je skládá
V programovacím jazyce se jedná o možnost volat stejné metody u různých objektů, aniž bychom věděli, jakého přesně jsou typu. Navíc může mít stejná metoda u různých objektů odlišný význam. To je možné díky tomu, že vždy známe společného předka těchto různých objektů. Tím může být třída, abstraktní třída nebo rozhraní.
// soubor Osoba.java
public class Osoba {
public int fce () {
return 10;
}
}
// soubor Zamestnanec.java
public class Zamestnanec extends Osoba {
public int fce () {
return 20;
}
}
Zamestnanec a = new Zamestnanec();
Osoba b = new Zamestnanec();
Osoba c = new Osoba();
System.out.println(a.fce()); // vypíše 20
System.out.println(b.fce()); // vypíše 20
System.out.println(c.fce()); // vypíše 10
V některých situacích je výhodné vytvořit jedinou bázovou třídu pro více tříd odpovídajících konkrétním objektům, i když tato samotná bázová třída žádnému konkrétnímu objektu neodpovídá. Může ovšem nést některá data a poskytovat metody, které jsou odvozeným třídám společné. Taková třída se pak nazývá abstraktní a je označena klíčovým slovem abstract. Překladač jazyka Java pak zajistí, že instanci abstraktní třídy nelze operátorem new přímo vytvořit, mohou se vytvářet pouze instance konkrétních tříd. Abstraktní třída může deklarovat některé společné metody a poskytovat jejich základní implementaci. Pokud odvozená třída takovou metodu nepředefinuje, pak se pro její instance použije implementace poskytnutá v bázové třídě. Mohou však nastat i situace, kdy skutečně vyžadujeme, aby odvozené třídy určitou metodu vždy definovaly. Takovou metodu pak také nazýváme abstraktní a označujeme klíčovým slovem abstract, navíc u ní není uvedeno tělo a hlavička metody je zakončena středníkem. Pokud odvozená třída některou abstraktní metodu neimplementuje, musí být také označena jako abstraktní. Tím je zajištěno, že instance konkrétních tříd mají všechny metody implementované.
Druhou velmi podobnou konstrukcí jsou rozhraní (klíčové slovo interface). Základním rozdílem je, že interface obsahuje pouze konstanty a metody bez těla (v tomto případě je neoznačujeme slovem abstract). Rozhraní je kontrakt, který specifikuje operace, které má třída splňovat, a který se již nezabývá tím, jak toho bude konkrétně dosaženo. Velkou výhodou rozhraní oproti abstraktním třídám je, že každá třída může implementovat až mnoho rozhraní, avšak vždy maximálně jednu třídu. Zatímco pro dědění tříd (a dědení interfaců mezi sebou) využíváme v hlavičce klíčové slovo extends, tak pro implementaci rozhraní používáme slovo implements.
Výčtové typy jsou speciální třídy zavedené pro větší bezpečí a pohodlí, v nejjednodušší variantě se definují (příklad):
enum Den { SUN, MON, TUE, WED, THU, FRI, SAT; }
for (Den d : Den.values( )) System.out.println( d.ordinal( )+ " " +d.name( ) );
- deklarativní - nepopisují jak se co má dělat, ale co má být výsledkem (př. HTML, Prolog: nepopisuje kroky výpočtu, ale fakta o problému a požadovaný výsledek)
- imperativní - zbytek (JAVA, C...), viz. imperativní programování otázka č. 5
Základní knihovny: 2.1.1 AWT - Abstract Window ToolKit
- první, těžké(heavyweight)
- vykreslení zajišťuje platforma – rychlejší, ne vždy vše funguje vše stejně
2.1.2 SWING
- doporučené
- nové komponenty (tree-view, list box,...),
- robustní
- Look and Feel - na platformě nezávislý a vypadá stejně na všech platformách a přitom respektuje i18n
- důsledné oddělení modelu od pohledu a řadiče
2.1.3 (SWT-Standard Widget Toolkit, Eclipse IBM)
- podobné AWT (platformově závislé vykreslení)
- mnoho rozšiřujících vlastností
Vyjímka je „nestandardní situace“:
- Situace, které jsou nestandardní, či které my považujeme za nestandardní, měli bychom reagovat a mužeme a dokážeme reagovat (
RuntimeException
)- Pokus o čtení z prázdného zásobníku
EmptyStackException
- Dělení nulou, indexování mimo rozsah pole, špatný formát císel
AritmeticException, NumberFormatException
- Pokus o čtení z prázdného zásobníku
- Situace, na které musíme reagovat, Java nás přinutí (
Exception, IOException
)- Odkaz na chybějící soubor
FileNotFoundException
- Odkaz na chybějící soubor
- Chyba v hardware, závažné chyby, nemůžeme reagovat (
Error
), (OutOfMemoryError,UnknownError
)- Chyba v JVM
- HW chyba
Obecne chyba vzniká pri porušení sémantických omezení jazyka Java. Bezpečnostní prvek Javy: zpracování chyb a nestandardních stavů není ponecháno jen na vůli programátora! Reakce na očekávané chyby se vynucuje na úrovni překladu, při nerespektování se překlad nepodaří.
Chyba při provádení programu v jazyku Java nemusí znamenat ukončení programu – chybu lze ošetrit a pokracovat dál. Při vzniku výjimky je automaticky vytvořen objekt, který nese informace o vzniklé výjimce. Mechanismus výjimek umožní přenést řízení z místa, kde výjimka vznikla do místa, kde bude zpracována. Oddělení "výkonné" části (try) od části "chybové" - catch.
Žádné použití throws nebo try-catch způsobí ukončení programu - CHYBA. Java sama ohlásí při překladu, které části jsou kritické a je je třeba ošetrit. Nejsou-li, pak minimálně "vyhozením" na vyšší úroven pomocí klauzule throws, jinak nedojde k překladu.
public static void main(String[] args) throws IOException { ... }
Závisí-li chod dalšího programu na korektní funkci metody, nemá cenu ji ošetrovat a přitom by činnost programu stejně nemohla pokračovat (proste tu hodnotu musíme mít a ne jen "nespadnutý" program!)
public static int XctiInt() throws Exception {
try {
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
return i;
} catch (Exception e) {
System.out.println("Chyba v udaji");
throw e; // predani vyse
}
}
Volající metoda musí opět použít try-catch, nebo výjimku throws výše.
Kompletní ošetření výjimky se provádí konstrukcí try - catch:
try { ... } catch (Exception e) { ... }
Klauzulí catch muže být i více pro ruzné typy výjimek. Pokud výjimka vyhovuje jedné větvi catch, tato se provede a ostatní už ne. Existuje také blok finally, ten se provede vždy, ať už výjimka nastane nebo ne.
Jestliže vznikne výjimka, potom JVM hledá odpovídající klauzuli, která je schopná výjimku ošetrit (tj. převzít rízení):
- pokud výjimka vznikla v bloku příkazu try, hledá se odpovídající klauzule catch v tomto příkazu, další príkazy bloku try se neprovedou a řízení se předá konstrukci ošetrující výjimku daného typu do místa ošetrení výjimky (tzv. handler = catch blok)
- pokud výjimka vznikne mimo příkaz try, předá se řízení do místa volání metody a pokračuje se podle předchozího bodu
- pokud taková konstrukce v těle funkce (metody, konstruktoru) není, skončí funkce nestandardně a výjimka se šírí na dynamicky nadřazenou úroven
- není-li výjimka ošetrena ani ve funkci main, vypíše se (na výstup) a program skončí
Pro rozlišení různých typů výjimek je v jazyku Java zavedena řada knihovních tříd, výjimky jsou instancemi těchto tříd.
Kontrolované výjimky musí být na rozdíl od nekontrolovaných explicitne deklarovány v hlavicce metody, ze které se mohou šírit, jedná se o výjimky trídy Exception
, je nutné je povinne obsloužit. Oznacují se též jako výjimky synchronní:
void m() throws Exception {
if (…) throw new Exception();
}
Nekontrolované výjimky jsou takové, které se mohou šírit z vetšiny metod a proto by jejich deklarování obtežovalo, tzv. asynchronní výjimky.
- bežný uživatel není schopen výjimku ošetrit – ze třídy Error. Třída Error je nadtřída všech výjimek, které převážne vznikají v důsledku softwarových či hardwarových chyb výpočetního systému a které vetšinou nelze v aplikaci smysluplně ošetrit.
MemoryOverflowError
- přetecení pamětiClassFormatError
- chybný formát byte-kódu
- chyby, které ošetrujeme podle potřeby, prekladac nekontroluje, zda tyto výjimky jsou ošetreny - podtřídy třídy
RuntimeException
. Nemusíme na ně reagovat, ale mužeme je predat "výše", překladač nás k reakci nenutí.ArithmeticException
- delení 0NullPointerException
- dereference odkazu null