Stáhnout prezentaci
Prezentace se nahrává, počkejte prosím
1
Programovací jazyk Java
Dana Nejedlová
2
Použitá a doporučená literatura
Pavel Herout: Učebnice jazyka Java, nakladatelství Kopp, České Budějovice, 2008, ISBN Sylabus předmětu Moderní programování s další literaturou Internet dle odkazů na snímcích
3
Historie 1990 – společnost Sun Microsystems řešila technologii programování vestavěných systémů. Jazyk C++ měl dostatečné možnosti je objektový (umožňuje modelování reality), ale je příliš náročný na zdroje, nemá management zdrojů (paměť, procesor), je závislý na platformě. Od poloviny 90. let minulého století se jazyk začal jmenovat Java a soustředil se na webové aplikace.
4
Charakteristika jazyka Java
objektovost Objektově orientované programování, dále jen OOP, umožňuje modelovat reálný svět, ale programování objektů je obvykle složité. Java umožňuje programovat objekty čitelnějším a bezpečnějším způsobem než jazyky původně procedurální, jako je C++. OOP zvyšuje flexibilitu, modularitu a možnost znovupoužití kódu. robustnost, ale … Chybí pointery, které můžou způsobit přepsání paměti a únik paměti. Java má mechanismus pro zachytávání chyb. podpora distribuovaných výpočtů Různé části programu mohou být vykonávány na různých počítačích. podpora vícevláknových aplikací (multithreading) Nutné pro práci s multimédii a sítěmi. interpetovanost Aplikace napsané v Javě jsou šířeny jako bytecode (druh mezijazyka) interpretovaný pomocí virtuálního stroje dostupného na mnoha platformách. nezávislost na platformě, portabilita (architecture neutrality) nižší rychlost, ale u GUI aplikací (kvůli kterým Java vznikla především) to nevadí a interpret Javy se vyvíjí (JIT compilation, HotSpot) bezpečnost Interpret kontroluje, zda program není škodlivý nebo nebezpečně napsaný. dynamičnost Nová část (třída - class) programu může být přidána bez nutnosti rekompilace.
5
Vznik programu v jazyce Java
Zdrojový kód je textový soubor s příponou „.java“. Překladač přeloží zdrojový kód do mezijazyka zvaného bytecode a uloží jej v souboru s příponou „.class“. součást JDK (Java Development Kit) Interpreter (Java Virtual Machine – JVM) ověří, že bytecode je bezpečný a spustí bytecode. součást JRE (Java Runtime Environment) JRE je podmnožinou JDK.
6
Různé typy programů aplikace aplety servlety
běžné programy, na které se zde omezíme aplety programy vestavěné ve webové stránce vykonávané klientem V současnosti jsou místo nich běžnější Flash, a Microsoft Silverlight. servlety aplety vykonávané serverem generované pomocí JSP (JavaServer Pages) Alternativou jsou ASP a PHP. dynamicky generované webové stránky
7
Vývojové nástroje Jsou závislé na platformě.
Překladač a standardní knihovny JDK SE - Standard Edition ( Knihovny se nazývají Java (Core) APIs a jsou dostupné na IDE – vývojová prostředí NetBeans, Eclipse
8
Struktura programu v jazyce Java
package helloworldapp; /** * <your name> */ public class HelloWorldApp { args the command line arguments public static void main(String[] args) { System.out.println("Hello World!"); } javac -d . HelloWorldApp.java Adresář, kam se uloží soubor HelloWorldApp.class, je helloworldapp. Dokumentační komentář Generuje se z něj dokumentace. Třída HelloWorldApp zapouzdřující metodu main Povinná hlavička hlavního programu – metody main Tělo metody main
9
Komentáře utrata = pocetPiv * 15; // jednořádkový komentář
/* Komentářový blok neboli komentář na několik řádků */ /** Dokumentační komentář, ze kterého se vytvoří dokumentace pomocí programu javadoc.exe */ Musí se vyskytnout bezprostředně před názvem třídy nebo metody. Používají se v něm značky javadoc -author -d MojeDokumentace Prvni.java příkaz, který vygeneruje dokumentaci do adresáře MojeDokumentace Komentáře nelze vnořovat do komentářů.
10
Identifikátory Java rozlišuje malá a velká písmena.
Identifikátory Java API se často liší jen velikostí písmen. Je nutno dodržovat konvence: třídy a rozhraní AccessibleContext, Double metody, proměnné, datové typy, klíčová slova accessibleContext, double V dokumentaci se proměnné od metod liší tím, že metody mají za jménem uvedeny prázdné kulaté závorky. balíky (packages) helloworldapp, java.lang konstanty MAX_VALUE Délka identifikátorů není omezena. Každý identifikátor musí začínat písmenem nebo podtržítkem.
11
Anotace (annotation) značky (metadata, tags) vkládané do zdrojového kódu a určené většinou pro různé nástroje, které s daným programem pracují. Mohou být zpracovány ve třech různých etapách práce s programem: při práci se zdrojovým kódem Využívá javadoc či překladač. Mohou být začleněny do přeloženého .class souboru a určeny pro nástroje pracující s těmito soubory před vlastním spuštěním programu, například nástroje připravující instalaci, zpracování za běhu programu. Začínají znakem Vyskytují se za řádkem s deklarací (tříd, metod, atributů, …) Podobnou práci konají typové modifikátory. Jsou určeny pro pokročilejší uživatele. Někdy je automaticky vygeneruje vývojové prostředí.
12
Anotace Java API @Deprecated (odmítaný, zavrhovaný)
Označení zastaralých nebo nebezpečných konstrukcí, které například musí být zachovány v knihovně tříd kvůli zpětné kompatibilitě. Použijeme-li ji, měli bychom v dokumentačním komentáři použít a za ní vysvětlit, proč je určitá konstrukce zavržena. @Override (překrytí) Je-li uvedena před metodami, které se mají při dědění překrýt, překladač upozorní na chyby v identifikátorech dědících metod. @SupressWarnings (potlačení varování) Při překladu by mělo být vždy zapnuté to, že překladač zobrazí všechna varovná hlášení. přepínač -Xlint:all překladače javac Vývojová prostředí umožňují vypnout určité druhy varování, ale lepší je možnost vypnout varování pro konkrétní úsek kódu, což je účel Když například ve větvích konstrukce switch máme důvod nepoužívat příkaz break, použijeme Názvy jednotlivých varování závisí na překladači, takže nejdříve si přečteme varování vypsané překladačem a potom jej použijeme jako argument anotace. Pokud chceme potlačit výpis více varovných hlášení najednou, zadávají se jejich názvy oddělené mezerami do jednoho textového řetězce, serial").
13
Základní (jednoduché, primitivní) datové typy
Vše v jazyce Java je buďto objekt nebo třída nebo součást objektu nebo třídy. Protože základní datové typy nejsou z pohledu Javy objekty, dává Java k dispozici ke každému základnímu datovému typu i jeho wrapper class (obalující třídu), která začíná na velké počátečním písmeno. Java na rozdíl od jazyka C definuje u každého typu jeho velikost. Základní datové typy se dělí na celočíselné, znakové, řetězcové, logické, reálné a prázdný datový typ void, který se používá jen u metod.
14
Celočíselné typy a jejich konstanty
Jsou pouze znaménkové ve dvojkovém doplňku. Mohou být zapsány ve třech číselných soustavách: desítkové – Číslo nesmí začínat nulou. osmičkové – Číslo začíná nulou. šestnáctkové – Číslo začíná 0x nebo 0X Dodržujeme styl 0xCD, nikoliv 0XCD, 0Xcd nebo 0xCd. Konstanty jsou implicitně typu int. Potřebujeme-li inicializovat typ long konstantou, použijeme na jejím konci znak „L“ (nebo „l“). Například long k = L; Jinak překladač hlásí chybu Integer literal out of range nebo dokonce odřízne nejvyšší bity. Inicializace typu byte konstantou v rozsahu typu byte (-128 až +127) je v pořádku. Například byte b = 123; Jinak překladač hlásí chybu Incompatible type.
15
Znakový typ a jeho konstanty
Je pouze jeden – char – a má velikost 2 byty. Je to z toho důvodu, že Java vnitřně pracuje se znaky ve znakové sadě Unicode. Unicode se od té doby rozšířilo na znaky zabírající více než 2 byty (supplementary characters). Tyto znaky jsou reprezentovány jako posloupnost 2 znaků typu char. Tato dvojice znaků uložená společně do jedné hodnoty datového typu int se nazývá kódový bod (code point). Chceme-li získat ze znaku jeho kódový bod, lze použít přetypování na int: int i = (int) 'A'; Znakové konstanty jsou vždy uzavřeny do apostrofů a mohou být představovány: jedním ASCII znakem, například 'A', '1', '%' posloupností '\uXXXX', kde XXXX jsou šestnáctkové číslice. Tento zápis se používá v případě akcentovaných znaků. Například „č“ se zapíše jako '\u010D', viz mapa Unicode – Latin Extended-A. Aby se zdrojový kód chybně nekonvertoval v prostředích s různým kódováním, je nutné toto kódování využívat. Může být také součástí identifikátorů, například int po\u010Det; // identifikátor počet escape sekvencí Místo '\a' (písknutí – alert) použijeme posloupnost '\0007'. (bell) Písknutí vykoná zvuková karta (pokud ji počítač má), nikoli speaker, jako tomu je v jazyce C. Nesmí být součástí identifikátorů. osmičkovým zápisem Vždy jsou nutné všechny tři osmičkové číslice, například '\007' je alternativní způsob zápisu písknutí.
16
Řetězcové konstanty Datový typ String je posloupností hodnot typu char uzavřenou do uvozovek. Tisk řetězce "Program kon\u010D\u00ED!\n\007" způsobí vypsání Program končí! odřádkování a písknutí. Java umožňuje automatické zřetězování dlouhých řetězcových konstant oddělených mezerami, tabelátory nebo novými řádkami, před kterými ale musí být znak „+“. "Takhle vypada " "velmi " "dlouhy retezec." Pokud je jedním z operandů operátoru „+“ řetězec, všechny ostatní operandy jsou na řetězec konvertovány. Výraz "abc" + 4 se vyhodnotí jako "abc4". Výraz "xyz" + (2 + 2 == 4) se vyhodnotí jako "xyztrue" Výraz 1 + "2.5" se vyhodnotí jako "12.5"
17
Logický typ a jeho konstanty
Používá se typ boolean. Reprezentuje jeden bit informace, ale velikost, kterou zabírá, není přesně definována. Může nabývat pouze dvou hodnot true (logická 1), false (logická 0). Logický typ je nepřevoditelný na celočíselné typy a naopak. Potřebujeme-li tuto operaci, je vhodné použít například: b = (i != 0); // převod hodnoty int na boolean i = (b) ? 1 : 0; // převod hodnoty boolean na int
18
Reálné typy a jejich konstanty
Java rozeznává typy float a double vyhovující standardu IEEE 754. Reálné konstanty se tvoří podle běžných zvyklostí, například 3.14, 15. , .84, 5e6, 7E23, -7E+23, -7E-23 Konstanty jsou implicitně typu double. Potřebujeme-li inicializovat typ float konstantou, použijeme na jejím konci znak „F“ (nebo „f“). Například float f = 3.14F;
19
Speciální hodnoty datových typů
Maximální a minimální hodnoty celočíselných i reálných typů lze získat pomocí konstant MIN_VALUE a MAX_VALUE, před které se dá ještě jméno příslušné třídy – Byte, Short, Integer, Long, Float, Double. Například minimální hodnotu typů int a float získáme takto: int i = Integer.MIN_VALUE; float f = Float.MIN_VALUE; Nenormální hodnoty typů float a double Pokud dělíme kladné či záporné číslo nulou, získáme POSITIVE_INFINITY či NEGATIVE_INFINITY, které je nutné testovat metodou isInfinite(). if (Float.isInfinite(f) == true) Pokud dělíme nulu nulou, získáme NaN, kterou je nutné testovat metodou isNaN(). if (Double.isNaN(d) == true)
20
Deklarace proměnných Překladač nedovolí použít hodnotu neinicializované proměnné. Neexistují globální proměnné, tedy proměnné, které „patří“ celému programu. V Javě každá proměnná patří buď třídě nebo instanci nebo metodě (funkci). Každá proměnná by měla být deklarována na samostatné řádce a okomentována. int celkovyPlat; // celkový plat Proměnné by měly být deklarovány na začátku metody a odděleny od příkazů prázdnou řádkou.
21
Deklarace proměnných s konstantní hodnotou
Tyto proměnné se běžně označují jako konstanty a deklarují se jako normální proměnné, ale navíc se použije klíčové slovo final. Tak lze deklarovat konstantu dvěma způsoby: final int MAX = 10; Konstantě MAX nelze znovu přiřadit jinou ani stejnou hodnotu. nebo final int MAX; // prázdná konstanta (blank final) … // nějaký kód MAX = 10; // inicializace, po které se již hodnota nedá změnit Tento způsob by se měl používat jen v odůvodněných případech. Jeden z těchto důvodů je použití ve statickém inicializačním bloku. Skutečné konstanty, které se dají použít i mimo třídu, musí být deklarovány vně jakékoliv metody takto: public class TridaSKonstantou { public static final int MAX = 10; …} Pak může být kdykoliv použita ve své třídě pod jménem MAX a mimo třídu pod takzvaným plně kvalifikovaným jménem TridaSKonstantou.MAX. Tím je zabráněno konfliktu jmen konstant, což se v jazyce C často stávalo při použití konstant vzniklých pomocí #define.
22
Deklarace výčtového typu enum
Výčtový typ je výhodný v případech, kdy pojmenované konstanty spolu souvisejí, chceme mít jistotu, že se použijí jen vyjmenované hodnoty a nic navíc, konstant je omezený počet (zhruba do 15), seznam konstant je za běhu programu neměnný. Typickým příkladem jsou dny v týdnu, názvy měsíců, pracovní zařazení, karty, směry, položky menu nebo rozbalovacího seznamu, argumenty programu s příkazovou řádkou. enum Okraj { HORNI, PRAVY, SPODNI, LEVY }; Typ enum je součástí Javy od JDK 1.5 a poskytuje mnohem víc možností než v jazyce C. Oracle, DevDaily, MindProd, Wakaleo 1, 2, singleton
23
Operátor přiřazení Stejně jako v jazyce C je přiřazení výraz a jeho hodnotou je hodnota přiřazená levé straně. Tudíž je možné jej používat v podmínkách, kde se však může zaměnit za operátor porovnání „==“. Java na rozdíl od C chyby typu if (j = 5) hlásí jako chybu, protože očekává výraz typu boolean a dostává hodnotu“ typu int. Základní operátor přiřazení je „=“. Rozšířené operátory přiřazení jsou typu „+=“.
24
Explicitní typová konverze
Operátor konverze se zapisuje ve formě kulatých závorek, uvnitř kterých je jméno datového typu, na který chceme přetypovat, například: char c1 = 'á'; char c2 = (char) (c1 + 1); následující znak v tabulce Přetypování má nejvyšší prioritu. Pokud přetypováváme výraz, je nutné jej uzavřít do závorek, jinak bude přetypován pouze první člen výrazu. Nutno provést v případě zužující konverze. Převádíme na typ s menším rozsahem.
25
Implicitní typová konverze
Překladač sám provede konverzi, když přiřazujeme do typu s větším rozsahem. Je to případ takzvané rozšiřující konverze. int j, i = ; float f; f = i; // implicitní rozšiřující konverze j = (int) f; // explicitní zužující konverze Při konverzi na float či double může dojít ke ztrátě přesnosti, proto nesmíme výsledek porovnávat s určitou hodnotou ale testovat, zda je absolutní hodnota odchylky výsledku od hodnoty menší než nějaká malá konstanta. Pořadí datových typů dle rozsahu od nejširšího po neužší: double, float, long, int, short, byte
26
Aritmetické výrazy Výraz ukončený středníkem se stává příkazem.
Aritmetické výrazy jsou tvořeny třemi druhy operátorů dle počtu operandů: unární unární „+“ a „-“, například j = -i; prefix a postfix inkrement „++“ a dekrement „--“ binární sčítání „+“, odčítání „-“, násobení „*“ Stejně jako jazyk C Java nehlásí přetečení a podtečení! dělení „/“ Jsou-li oba operandy celočíselné je dělení celočíselné, Java hlásí chybu při dělení nulou. zbytek po celočíselném dělení (modulus) „%“ ternární alternativa k příkazu if – else
27
Relační operátory Vytvářejí se z nich booleovské výrazy pro řízení běhu programu. Stejné jako v jazyce C: rovnost „==“, nerovnost „!=“, negace „!“, menší „<“, menší nebo rovno „<=“, větší „>“, větší nebo rovno „>=“, logický součin – AND „&&“, logický součet – OR „||“ Pro urychlení programu je vhodné dávat na první místo (doleva) ten výraz, jehož hodnota je u operátoru && nejpravděpodobnější nepravda (false), u operátoru || nejpravděpodobnější pravda (true). Úplné vyhodnocování logických výrazů logický součin „&“, logický součet „|“ Využívá se v případě výrazů s vedlejšími účinky, které musí být vždy provedeny, například if (i == 2 & ++j == 3)
28
Bitové operace Manipulují s jednotlivými bity čísla.
Stejně jako v jazyce C argumenty bitových operací nesmějí být proměnné typů float a double. bitový součin „&“, bitový součet „|“ Dle typu operandů Java pozná, zda jde o bitový součin nebo o úplné vyhodnocování logického výrazu. Bitový exklusivní součet – XOR „^“ Znaménkový posun doleva „<<“ a doprava „>>“ Při posunu doprava se zachovává znaménkový bit. Znaménkový bitový posun doprava se používá se pro rychlé dělení mocninou čísla 2 se zachováním znaménka. Na rozdíl od jazyka C je znaménkové chování v definici jazyka Java. V jazyce C je implementačně závislé. Neznaménkový posun doprava „>>>“ Při posunu doprava se nezachovává znaménkový bit. Správně funguje jen pro typy int a long. Jedničkový doplněk – negace „~“ unární operátor
29
Precedence operátorů V definici jazyka Java tabulka priorit operátorů není, protože je definována gramatikou jazyka. Různé zdroje uvádějí tabulky, které se v nepříliš podstatných detailech liší. Z důvodu bezpečnosti a zvýšení srozumitelnosti kódu se doporučuje v hojné míře používat závorky.
30
Terminálový vstup a výstup
Ve skutečných programech jsou mnohem vhodnější grafická uživatelská rozhraní. Program s terminálovým V/V není 100% přenositelný na všechny platformy, protože některé operační systémy vůbec terminálový V/V nemají. Nebudeme používat třídu java.io.Console, protože neumí pracovat se soubory.
31
Metoda System.out.print()
Převede proměnnou, která je jejím parametrem, na řetězec představující dekadickou hodnotu proměnné a ten vytiskne. Parametrem může být i řetězcová konstanta i = 4; j = 7; System.out.print(i + " + " + j + " = " + (i + j) + "\n"); Vypíše i + j = 11 a odřádkuje. Pokud by místo "\n" bylo '\n', znak '\n' se konvertuje na řetězec "\n", ale pouze, pokud je tam nějaký řetězec, viz poslední příklad. System.out.println(i + " + " + j + " = " + i + j); Vypíše i + j = 47 a odřádkuje. Metoda println() je pro odřádkování lepší než použití řetězce "\n". System.out.println("Pracovali na 100 %."); Znak % není při výpisu nutné nijak zdvojovat. System.out.println("Toto je \"backslash\": '\\'."); Znak \ se zdvojuje, uvozovky " musí předcházet znak \, Apostrof ' se tiskne jako normální znak. System.out.print(i + '\n'); Vypíše 14 a neodřádkuje, protože se sečetla hodnota i, což je 4, s kódem znaku '\n', což je 10, a výsledek se převedl na řetězec. char c = 'A'; System.out.println("Znak " + c + " má ASCII hodnotu " + (int) c); Chceme-li při tisku změnit typ proměnné, můžeme použít přetypování. System.out.print("\b\b \n"); Dva znaky „návrat kurzoru“, mezera, nový řádek Za posledním prvkem generovaným cyklem se umaže oddělovač. Nefunguje to při přesměrování výstupu do souboru
32
Metoda System.out.format()
Je součástí Javy od JDK 1.5 a je navázána na třídu java.util.Formatter. Pod odkazem je dokumentace z Java API s možnými parametry. Přebírá všechny formátovací možnosti funkce printf() z jazyka C a v mnoha místech je velmi elegantně rozšiřuje. Kromě ní existuje i metoda System.out.printf(), která se však nedá využít pro tvorbu GUI. Ve třídě String existuje statická metoda format() vracející typ String, využitelná při tvorbě GUI, která má stejná pravidla jako metoda System.out.format(). Prvním parametrem musí být formátovací řetězec a za ním musí být tolik parametrů, kolik je ve formátovací řetězci znaků %, které neuvádějí escape sekvenci %n. Konec řádku \n je v Unicode znak '\u000A', ale používat pouze \n. %n konec řádků závislý na platformě Zápis konce řádku do textového souboru
33
Metoda System.out.format() příklady
Základní příklad System.out.format("i = %d, f = %f%n", i, f); Vypíše i dekadicky a f jako reálné číslo a odřádkuje. Výpis celého čísla v desítkové soustavě zarovnání doprava doplněním mezer zleva "i = %7d" zarovnání doleva doplněním mezer zprava "i = %-7d" zarovnání doprava a výpis i s kladným znaménkem "i = %+7d" zarovnání doprava doplněním nul "i = %07d" vypsání s oddělovačem řádů závislým na lokalitě "i = %,7d" V české lokalitě může být oddělovačem mezera ve formě znaku čtvrt čtverčíku, který není v každém fontu, a potom se může vytisknout nesmyslný znak. Výpis celého čísla v jiných soustavách výpis v osmičkové soustavě "i = %o" výpis v šestnáctkové soustavě "i = %x" nebo "i = %X" Výpis znaku System.out.format("Znak %c má ASCII hodnotu %d%n", c, (int) c); // přetypování je nutné Výpis reálného čísla typu float nebo double běžným způsobem "f = %f" V české lokalitě je desetinným oddělovačem čárka. běžným způsobem s desetinným oddělovačem tečka "f = %g" Čísla s vysokým počtem míst budou zaokrouhlena nebo vypsána v semilogaritmickém tvaru. v semilogaritmickém tvaru "f = %e" s určitým počtem míst celkem a za desetinným oddělovačem "f = %7.2f" se zarovnáním a oddělovačem řádů "f = %-,7.2f" Výpis řetězce velkými písmeny (funguje i pro akcentované znaky) "s = %S" (malými písmeny "s = %s")
34
Formátovaný vstup Od JDK 1.5 lze používat třídu java.util.Scanner.
import java.util.Scanner; // import třídy z knihovny import java.util.Locale; // import třídy z knihovny // předchozí 2 řádky je možné nahradit příkazem import java.util.*; public class Nacitani { public static void main(String[] args) { Scanner sc = new Scanner(System.in); // inicializace čtení sc.useLocale(Locale.US); // Desetinný oddělovač bude tečka. int i = sc.nextInt(); // metoda pro čtení celého čísla double d = sc.nextDouble(); // metoda pro čtení reálného čísla char c = sc.nextLine().charAt(0); // Zde dojde k běhové chybě. /* Musíme přečíst celou řádku a vyseparovat první znak. */ String s1 = sc.next(); // načtení řetězce do prvního bílého znaku /* Pokud řetězec začíná na bílé znaky, tak jsou zahozeny. */ String s2 = sc.nextLine(); // načtení celého řádku /* Znak(y) konce řádku jsou přečteny ale zahozeny. */ } Proč jsme nemuseli nic importovat při použití metody System.out.format? Protože patří do balíku java.lang, který je importován vždy implicitně.
35
Problém vyprázdnění vstupu
Po každém čtení pomocí metod nextInt(), nextDouble() a next() je třeba pomocí volání metody nextLine() vyprázdnit buffer klávesnice příkazem sc.nextLine();
36
Řídící struktury Budou probrány pouze odlišnosti od jazyka C.
Zásady formátování jsou stejné jako u jazyka C.
37
Podmínka: příkaz if – else
V závorce za if musí být výraz s výslednou hodnotou typu boolean. Z tohoto důvodu není možné napsat častou céčkovskou chybu se zapomenutými závorkami kolem přiřazení. if (c = ctiZnak() != 'A') // syntaktická chyba if ((c = ctiZnak()) != 'A') // správně Podmíněný výraz – ternární operátor Umožňuje psát kratší programy for (int i = 1; i <= 100; i++) System.out.print(i + ((i % 10 == 0) ? "\n" : " ")); Za každým desátým číslem bude odřádkováno.
38
Skoky Java nezná příkaz goto, i když je goto rezervované slovo.
Příkazy break a continue společně s aparátem pro zachycení výjimek jej dostatečně a hlavně bezpečněji nahrazují. Příkazy break a continue mají verzi s návěštím a bez návěští. Příkaz break (na rozdíl od continue) ukončuje také právě prováděný blok, ve kterém nemusí být žádný cyklus, nebo příkaz switch.
39
Příkaz while a do – while
V závorce s ukončovací podmínkou musí být výraz s výslednou hodnotou typu boolean. while (1) // syntaktická chyba Cyklus s ukončující podmínkou public static void main(String[] args) { Scanner sc = new Scanner(System.in); char c; while ((c = sc.nextLine().charAt(0)) != 'z') { if (c >= 'a') System.out.print(c); } System.out.println("\nCteni znaku bylo ukonceno."); Nekonečný cyklus ukončený skokem bez návěští while (true) { // nekonečná smyčka if ((c = sc.nextLine().charAt(0)) < 'a') continue; // zahození velkých písmen atd. if (c == 'z') break; // zastavení po načtení znaku 'z' System.out.print(c); // tisk znaku
40
Příkaz for V Javě se velmi často deklaruje řídící proměnná cyklu přímo v hlavičce cyklu. for (int i = 1; i <= 10; i++) System.out.println(i); V jazyce C je to dovoleno počínaje normou C99, ale ne všechny nové překladače to dovolují, takže se to nedoporučuje. Java nezná operátor čárka, pokud není v inicializační nebo v iterační části for cyklu. Správné použití int i, faktorial; for (i = 1, faktorial = 1; i <= 5; i++) faktorial *= i; Nesprávné použití for (int i = 1, faktorial = 1; i <= 5; i++) Proměnná faktorial má oblast platnosti pouze v cyklu for. Například po následném příkazu System.out.println(faktorial); nastane chyba překladu. Má ještě jednu variantu zvanou „for-each“, která se užívá při práci s poli, typem enum a kontejnerem objektů.
41
Příkaz for s příkazem continue s návěštím
Výpis indexů matice pod hlavní diagonálou navesti: for (int n = 0; n < 4; n++) { for (int m = 0; m < 4; m++) { if (m == n) { System.out.println(); continue navesti; } System.out.print(m + "-" + n + " ");
42
Příkaz for s příkazem break s návěštím
Výskok z vnořených (zahnízděných) cyklů boolean jeChyba = false; chyba: { for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { for (int k = 0; k < 10; k++) { if (x[k] == 0) { jeChyba = true; break chyba; } a[i][j][k] = a[i][j][k] + b[j] / x[k]; System.out.println("Bez chyby."); if (jeChyba) { System.out.println("Nulovy delitel!");
43
Příkaz switch Počet větví je neomezen.
Dle ANSI C jich musí být alespoň 257. Výraz, podle kterého se rozhoduje, musí být pouze typu char, byte, short nebo int. Nesmí být typu long, float, nebo double. Větve končí příkazem break nebo dojde k vykonání příkazů v další větvi. Viz Příkaz continue s příkazem switch nespolupracuje. Pokud by byl příkaz switch vnořen do while cyklu, skočí příkaz continue na konec tohoto while cyklu a potom se bude while cyklus opakovat, ale je to nepřehledné.
44
Příkaz return Příkaz return ukončí provádění metody (funkce), která jej obsahuje. V metodě main() ukončí příkaz return celý program. Často se pomocí return vrací nějaká hodnota, jejíž typ záleží na typu metody. Metoda System.exit() ukončí bezprostředně program bez návratu do funkce volající. Používá se jen v aplikacích, nikoliv v apletech.
45
Metody Metoda je v OOP termín pro podprogram neboli funkci.
Metody v Javě se dělí na metody třídy neboli statické metody a metody instance. Zatím se budeme věnovat pouze společným znakům obou podskupin metod. Tyto společné znaky budou vysvětlovány na statických metodách, aby bylo možné ověřit popisované skutečnosti bez nutnosti použití objektů.
46
Oblast řešená metodou Překladač neomezuje délku názvu metody a délku jejího kódu. Je však vhodné psát takové metody, které provádějí pouze jednu činnost dobře vystiženou názvem metody. Délka těla metody by neměla přesáhnout cca 20 řádek. Jméno metody by mělo být krátké a výstižné, viz identifikátory. Pokud nejsme schopni přiřadit metodě rozumné krátké jméno, je to pravděpodobně tím, že se snažíme, aby prováděla více než jednu činnost. To svědčí o špatné dekompozici problému.
47
Deklarace metody Deklarace metody zahrnuje
hlavičku metody, tj. jméno metody, Není ukončena středníkem. Levá složená závorka je na téže řádce a je oddělena jednou mezerou. typ návratové hodnoty, Nemůže být vynechán. případně i typy a jména formálních parametrů, tělo, ve kterém je uložen kód metody. V Javě nevadí, je-li deklarace metody až za místem volání metody. Není tedy nutné uvádět funkční prototypy. Každá metoda musí patřit do nějaké třídy. Podle jména této třídy se musí jmenovat i soubor, ve kterém je uložena.
48
Příklad metody s parametry
deklarace statické metody static int max(int a, int b) { if (a > b) return (a); // možno také return a; else return (b); } volání statické metody uvnitř jedné třídy x = max(10 * i, j - 15);
49
Metoda bez parametrů příklad metody, která přečte dvě celá čísla z klávesnice a vrátí jejich součet static int nactiASecti() { Scanner sc = new Scanner(System.in); int a, b; a = sc.nextInt(); b = sc.nextInt(); return (a + b); } volání metody j = nactiASecti(); Chybné volání metody j = nactiASecti; bez závorky je syntaktickou chybou. Překladač vypíše chybu Variable nactiASecti not found in class. Není možné uvést místo neexistujících formálních parametrů klíčové slovo void, např. static int nactiASecti(void) { Překladač hlásí chybu <identifier> expected.
50
Metoda bez návratového typu
Jejím návratovým typem je void. Příkaz return se v ní používá pouze pro nucené ukončení metody před dosažením jejího konce po nějaké podmínce. procedura s parametry static void tiskPenez(int koruny) { System.out.println("Cena: " + koruny + " Kc"); } volání metody tiskPenez(a + b); procedura bez parametrů static void tisk() { System.out.println("Ahoj"); tisk();
51
Metoda s více parametry různých typů
static double secti(int a, int b, double c) { return a + b + c; } static double secti(int a, b, double c) { Chyba: Typ musí být uveden u každého parametru. Ačkoliv počet parametrů není nijak omezen, je vhodné nepřesáhnout počet pěti parametrů. Při větším počtu je metoda nepřehledná a svědčí to o špatné dekompozici problému. Java provádí důslednou kontrolu typu a počtu parametrů a kontrolu návratového typu metody. Pokud typ nesouhlasí, dojde buďto k implicitní typové konverzi nebo je nutné provést explicitní typovou konverzi.
52
Rekurzivní metody Rekurzivní algoritmy požíváme v případech, kdy neexistuje jednoduché řešení pomocí cyklů. Například faktoriál lze řešit rekurzívně i for cyklem, proto je lepší zvolit for cyklus, ale prohledávání adresářů je vhodné řešit pouze rekurzívně. Metoda pro výpočet faktoriálu public class Faktorial { public static void main(String[] args) { System.out.println("20! = " + faktorial(20)); } public static long faktorial(long n) { if (n > 1) return n * faktorial(n - 1); else return 1;
53
Způsoby předávání skutečných parametrů metod
Java umožňuje pouze jeden způsob předávání parametrů a to hodnotou (call-by-value). To znamená, že při volání metody se do jejích formálních parametrů zkopírují skutečné parametry a jejich změna uvnitř metody se po opuštění metody ztrácí. Možnost volání odkazem Java neřeší pomocí ukazatelů (pointerů) ale s využitím prostředků OOP. Stejně tak Java neumožňuje využívat metod s proměnným počtem parametrů. Tento handicap se řeší pomocí přetížení metod.
54
Přetížené metody (overloaded)
metody, které mají stejná jména ale různé hlavičky lišící se počtem, typem nebo pořadím formálních parametrů Metodu nelze přetížit pouhou změnou typu návratové hodnoty. Jinak překladač hlásí chybu Duplicate definition. Kdykoliv je přetížená metoda volána, kompilátor vybere tu z metod, která přesně vyhovuje počtu, typům a pořadí skutečných parametrů. Lze se tak vyhnout nutnosti přetypovávat skutečné parametry na typ formálních parametrů. dvojnásobně přetížená metoda obdelnik() static long obdelnik(long i) { return i * i; } // static double obdelnik(long i) { return (double) (i * i); } // chyba Duplicate definition static double obdelnik(double i, double j) { return i * j; } public static void main(String[] args) { System.out.println(obdelnik(2L)); System.out.println(obdelnik(3.0, 7.0)); } Můžeme vypočítat obsah čtverce, když zadáme jeden parametr typu long nebo obsah obdélníku, když zadáme dva parametry typu double. Další možnosti by byly umožněny po napsání více metod s dalšími kombinacemi počtu a typů parametrů.
55
Nelokální proměnné Java nemá globální proměnné, protože každá proměnná musí patřit nějaké třídě. Termín „globální“ patří do procedurálního programování a ne do OOP. Nelokální proměnné jsou deklarovány vně všech metod v rámci určité třídy a jsou potom přístupné všem těmto metodám. Nezáleží na pořadí deklarací metod a proměnných, stejně jako nezáleží na pořadí deklarací volajících se metod, protože třída představuje jeden prostor jmen neboli oblast viditelnosti. Pro zvýšení přehlednosti se v třídě deklarují nejdříve proměnné a potom metody. Dělí se na proměnné třídy, též statické proměnné, Jsou uvozeny klíčovým slovem static. proměnné instance. Nemohou je přímo využívat statické metody. Jsou implicitně inicializovány na nulu. int – 0, float – 0.0, char – '\u0000', boolean – false Je však dobrým zvykem nespoléhat se na tuto službu a u všech proměnných, které mají být inicializovány, tuto inicializaci výslovně uvést. Stejně jako u konstant jsou přístupné v jiných třídách pod jménem své obalující třídy za nímž je tečka a za ní identifikátor proměnné.
56
Příklad s nelokální proměnnou
Inicializaci čtení je lepší provést mimo metodu volanou vícekrát, protože máme pouze jednu klávesnici. Lokální inicializace by mohla vést k problémům při složitějším použití. public class ScannerStatickaPromenna { static Scanner scanner = new Scanner(System.in); public static int nactiCislo(int poradi) { /* Scanner scanner = new Scanner(System.in); Sem radši ne. */ System.out.print("Zadej " + poradi + ". cislo: "); return scanner.nextInt(); } public static void main(String[] args) { int i1 = nactiCislo(1); int i2 = nactiCislo(2); int i3 = nactiCislo(3); System.out.println("Soucet je: " + (i1 + i2 + i3));
57
Lokální proměnné proměnné deklarované uvnitř metody
Jsou viditelné pouze ve své metodě. Funkce main() není výjimkou. Pokud jsou deklarovány v bloku, pak se jejich viditelnost omezuje pouze na tento blok. Jejich identifikátory nesmí být stejné jako identifikátory jiných lokálních proměnných metody. Rozsah jejich platnosti je od místa deklarace do konce metody nebo bloku. Nejsou automaticky inicializovány. Překladač však použití neinicializované proměnné hlásí jako syntaktickou chybu, např. Variable i might not have been initialized, viz Deklarace proměnných. Nelze u nich použít klíčové slovo static ani public. Lze je označit jako final, ale tento způsob se příliš nepoužívá. Má-li nelokální proměnná stejné jméno jako lokální proměnná, pak ji lokální proměnná ve své metodě zastiňuje. K nelokální proměnné lze potom přistoupit pomocí plně kvalifikovaného jména.
58
Referenční datový typ Java obsahuje dva neprimitivní datové typy a to pole a objekty. Proměnné těchto typů jsou označovány jako referenční. Někdy se tak označují pouze proměnné s datovým typem třídy. Referenční proměnné se využívají podobně jako ukazatele v jiných programovacích jazycích. Není však možné považovat hodnotu referenční proměnné za adresu do paměti. Referenční proměnná po své deklaraci nereprezentuje žádná data. Pole a objekty vznikají dynamicky pomocí speciálního příkazu a zanikají, jakmile na ně neexistuje odkaz. Odkaz (reference) je konkrétní hodnota referenční proměnné. Hodnota neplatného (neexistujícího) odkazu je hodnota konstanty null. null je klíčové slovo nikoli konstanta a proto se píše malými písmeny.
59
Pole deklarace pole prvků typu int vznik pole deklarace s inicializací
int[] poleInt; vznik pole poleInt = new int[20]; Bez tohoto příkazu dostaneme chybové hlášení Variable poleInt might not be initialized. deklarace s inicializací int[] poleInt = new int[20]; Po inicializaci pomocí operátoru new mají prvky pole nulové hodnoty, hodnoty false v případě pole typu boolean a hodnoty null v případě pole referenčních proměnných. pole vzniklé pomocí statického inicializátoru int[] prvocisla = { 1, 2, 3, 5, 7, 11 }; Prvky pole nejsou konstantní. Délku pole lze zjistit pomocí členské proměnné se jménem length. for (int i = 0; i < poleInt.length; i++) { // Nepoužívat nic jiného než length. System.out.print(poleInt[i] + " "); } Počáteční prvek pole má vždy index [0] a poslední prvek má index [jmenoPole.length - 1]. Při překročení mezí pole program reaguje vygenerováním výjimky ArrayIndexOutOfBoundsException. Indexy pole mohou být pouze konstanty, proměnné nebo výrazy typu int. Typ short, byte, nebo char se automaticky konvertuje na int. Typ long nelze.
60
Průchod všemi prvky pole
Sečtení všech prvků celého pole public class ForEach { public static void main(String[] args) { int[] poleInt = { 1, 3, 5, 7 }; int suma = 0; for (int hodnota : poleInt) { suma += hodnota; } System.out.println("Suma = " + suma); Zpracovávaný prvek pole se zkopíruje do pomocné proměnné hodnota. Proto není možné pomocí konstrukce for-each měnit prvky pole.
61
Seřazení pole a jeho výpis
Výpis pole je užitečný zejména při ladění. import java.util.Arrays; public class SerazeniPole { public static void main(String[] args) { int[] poleInt = { 1, 7, 5, 3 }; System.out.println(Arrays.toString(poleInt)); Arrays.sort(poleInt); } Složitější třídění vyžaduje práci s rozhraním a kolekcemi objektů.
62
Dvourozměrná pole Obdélníkové pole
int[][] a = new int[5][4]; System.out.println("Pocet radek: " + a.length); System.out.println("Pocet sloupcu: " + a[0].length); for (int i = 0; i < a.length; i++) { for (int j = 0; j < a[i].length; j++) { a[i][j] = i * 10 + j; System.out.print(a[i][j] + " "); } System.out.println(); Dvourozměrné pole s proměnlivou délkou řádek int[][] a = new int[4][]; a[i] = new int[i + 1];
63
Inicializace statického dvourozměrného pole
Obdélníkové pole int[][] b = {{ 1, 2, 3 }, { 11, 12, 13 }, { 21, 22, 23 }}; Dvourozměrné pole s proměnlivou délkou řádek int[][] c = {{ 1, 2, 3 }, { 11, 12 }, { 21 }};
64
Vícerozměrná pole Trojrozměrná a vícerozměrná pole se vytvářejí analogicky dvourozměrným. V případě, že se pole vytváří po částech, musíme nejdřív vytvořit první rozměr (index), potom druhý a tak dál, nelze rozměry přeskakovat. int[][][] d = new int[4][][5]; // chyba Vícerozměrná pole nezabírají spojitou část paměti, což má za následek pomalejší přístup k nim. Proto se pro zrychlení (typicky grafických aplikací) používá jednorozměrné pole s výpočtem ofsetu. final int RADKY = 24; final int SLOUPCE = 80; byte[] obrazovka = new byte[RADKY * SLOUPCE]; for (int i = 0; i < RADKY; i++) { for (int j = 0; j < SLOUPCE; j++) { obrazovka[i * SLOUPCE + j] = 0; … další možnosti zrychlení
65
Třídy a objekty Třída (class), objektový typ, modul Objekt (object)
základní stavební kámen v OOP soubor dat a podprogramů data = datové složky = atributy členské proměnné (member variables) a konstanty Je v nich uložen stav objektu. podprogramy = metody (methods) Manipulují s daty, čímž mění stav objektu. Popisují schopnosti objektu. Objekt (object) datový prvek vytvořený podle vzoru třídy instance třídy Proměnné třídy se deklarují s klíčovým slovem static, instance se deklarují bez klíčového slova static. V Java API jsou označovány jako field (pole, oblast), což většinou neznamená array.
66
Deklarace třídy Program v Javě obsahuje vždy alespoň jednu třídu.
Třídy označené jako public musí být uloženy v samostatném stejnojmenném souboru s příponou java, měly by (ale nemusí) obsahovat metodu main(), která volá ostatní metody své třídy. Když je metoda main() označena jako static, je možné ji volat i bez existující instance její mateřské třídy, tedy bez vytvoření objektu.
67
Deklarace třídy Obdelnik
public class Obdelnik { public int sirka; public int vyska; public int obvod() { return (2 * (sirka + vyska)); } public int obsah() { return (sirka * vyska); public double delkaUhlopricky() { double pom; pom = (sirka * sirka) + (vyska * vyska); return Math.sqrt(pom);
68
Vytvoření objektu Deklarujeme třídu, například Obdelnik.
Deklarujeme referenční proměnnou typu Obdelnik. Odkazuje na instanci typu Obdelnik. například Obdelnik obd; Pomocí příkazu new vytvoříme v paměti objekt typu Obdelnik, získanou referenci na něj přiřadíme do proměnné, například obd = new Obdelnik(); Na rozdíl od polí se objekty tvoří vždy jen dynamicky. Od této chvíle máme přes referenční proměnnou obd k dispozici nelokální proměnné třídy Obdelnik nastavené implicitně na nulu. Proměnné sirka a vyska jsou proměnné instance neboli instanční proměnné. Každá instance má své vlastní proměnné sirka a vyska. Metody obvod(), obsah() a delkaUhlopricky() se nedají použít samostatně, jako to bylo možné u statických metod.
69
Vytvoření objektu obd Kde se má objekt vytvořit?
jednodušeji v metodě main() třídy Obdelnik častěji v jiné třídě public class Obdelnik { … public static void main(String[] args) { Obdelnik obd = new Obdelnik(); obd.vyska = 5; obd.sirka = 3; System.out.println("Obvod je: " + obd.obvod()); }
70
Konstruktor Specializovaná metoda umožňující při vytvoření objektu inicializovat hodnotu jeho dat v parametrech příkazu new. Deklarace třídy Obdelnik s využitím konstruktoru public class Obdelnik { public int sirka; public int vyska; /* začátek konstruktoru */ public Obdelnik(int parSirka, int parVyska) { sirka = parSirka; vyska = parVyska; } /* konec konstruktoru */ public int obvod() { return (2 * (sirka + vyska)); } public int obsah() { return (sirka * vyska); } public static void main(String[] args) { Obdelnik obd = new Obdelnik(5, 3); /* využití konstuktoru */ System.out.println("Obvod je: " + obd.obvod()); } Konstruktor má vždy stejné jméno jako jeho třída. Konstruktor nemá návratovou hodnotu a nesmí se v něm uvést návratový typ void. Pokud potřebujeme předčasně ukončit práci konstruktoru, použijeme příkaz return bez parametrů. if (parSirka == 0) return; Nechceme-li vytvářet konstruktor, je možné proměnné deklarovat s inicializací. public int sirka = 5; public int vyska = 3; …
71
Implicitní parametr metody this
Má jej každá metoda instance. Odkazuje na „tuto instanci“. Využívá se v případě, že chceme pojmenovat parametry a lokální proměnné metody stejně, jako jsou pojmenovány proměnné instance, protože nechceme vymýšlet různé zkomoleniny jen pro odlišení jmen. public class Obdelnik { public int sirka; // proměnná instance public int vyska; // proměnná instance /* začátek konstruktoru */ public Obdelnik(int sirka, int vyska) { this.sirka = sirka; this.vyska = vyska; } /* konec konstruktoru */ … }
72
Přetížení konstruktorů
Chceme mít možnost inicializovat vznikající objekt různými způsoby. public class Obdelnik { public int sirka; public int vyska; public Obdelnik(int sirka, int vyska) { /* inicializace parametry */ this.sirka = sirka; this.vyska = vyska; } public Obdelnik(Obdelnik o) { /* inicializace jiným objektem */ sirka = o.sirka; // this není nutné, this.vyska = o.vyska; // ale zlepší čitelnost. public Obdelnik() { /* inicializace bez parametrů */ sirka = 1; vyska = 1; public int obvod() { return (2 * (sirka + vyska)); } public int obsah() { return (sirka * vyska); } public static void main(String[] args) { Obdelnik obd = new Obdelnik(5, 3); Obdelnik jiny = new Obdelnik(obd); Obdelnik jednotkovy = new Obdelnik(); System.out.println("Obvod je: " + obd.obvod()); System.out.println("Obvod je: " + jiny.obvod()); System.out.println("Obvod je: " + jednotkovy.obvod()); Pokud je vytvořen libovolný konstruktor s parametry, pak musíme vytvořit i případný konstruktor bez parametrů, který nemusel být vytvořen v předešlém příkladu. Nemá-li třída konstruktor, překladač vytvoří implicitní konstruktor (default constructor), jehož úkolem je vyřešit vztahy s předky.
73
Využití this pro přístup ke konstruktoru
Pomocí this bez tečky ale se závorkami může konstruktor vyvolat jiný konstruktor stejné třídy. Volání this se vztahuje na ten konstruktor, jehož parametry vyhovují co do počtu a pořadí typů. Volání jiného konstruktoru pomocí this musí být první příkaz ve volajícím konstruktoru. Důvodem je, že v konstruktoru se automaticky volá konstruktor rodičovské třídy. V následujícím příkladu 2. a 3. konstruktor volá 1. konstruktor public Obdelnik(int sirka, int vyska) { // 1. konstruktor this.sirka = sirka; this.vyska = vyska; } public Obdelnik(Obdelnik o) { // 2. konstruktor this(o.sirka, o.vyska); /* místo this.sirka = o.sirka; this.vyska = o.vyska; */ public Obdelnik() { // 3. konstruktor this(1, 1); /* místo sirka = 1; vyska = 1; */ Využití je pro zjednodušení zápisu inicializace velkého počtu parametrů.
74
Volání metod jinými metodami téže třídy nebo konstruktorem
Metody instance mohou libovolně volat jiné metody instancí z dané třídy a metody třídy. Konstruktor třídy může volat všechny metody a konstruktory téže třídy. Metoda smí konstruktor volat pouze prostřednictvím operátoru new. public void nastavSirku(int sirka) { this.sirka = sirka; } public Obdelnik(int sirka, int vyska) { nastavSrirku(sirka); /* volání metody konstruktorem */ this.vyska = vyska;
75
Použití proměnné třídy v objektech
Proměnné třídy deklarované s klíčovým slovem static se neduplikují v instancích této třídy, tedy existují v programu jen v jediné kopii a lze je využít, i když nebyly vytvořeny žádné objekty, viz též metoda main(). Zjištění počtu vytvořených objektů public class Obdelnik { public static int pocetObdelniku = 0; // proměnná třídy public int sirka; // proměnná instance public int vyska; // proměnná instance public Obdelnik(int sirka, int vyska) { // konstruktor Obdelnik.pocetObdelniku++; /* možno též (nepřehledně) this.pocetObdelniku++; nebo pocetObdelniku++; */ this.sirka = sirka; this.vyska = vyska; } public static void main(String[] args) { Obdelnik obd1 = new Obdelnik(5, 3); Obdelnik obd2 = new Obdelnik(1, 2); Obdelnik obd3 = new Obdelnik(4, 6); System.out.println("Bylo vytvořeno " + pocetObdelniku + " obdélníků."); Proměnnou pocetObdelniku je možné změnit i mimo konstruktor, což nemusí být dobré. Řešením je omezit přístupová práva k této proměnné klíčovým slovem private.
76
Použití statických metod v objektech
Metody třídy (nebo též statické metody) deklarované s klíčovým slovem static nevytvářejí vlastní objekty, což je výhodné pro úlohy, které se obejdou bez vlastních objektů. Statická metoda ze třídy z Java Core API public class Obdelnik { /* Proměnné, konstruktory a metody vynechány. */ public double delkaUhlopricky() { double pom; pom = (sirka * sirka) + (vyska * vyska); return (Math.sqrt(pom)); // sqrt() je metoda třídy Math. } public static void main(String[] args) { Obdelnik obd = new Obdelnik(6, 8); System.out.println("Uhlopricka je " + obd.delkaUhlopricky()); Statická metoda z téže třídy pro autorizovaný přístup ke statické proměnné private static int pocetObdelniku = 0; // Proměnná třídy, která díky private nebude přístupná vně třídy /* Další proměnné, konstruktory a metody vynechány. */ public static int kolikObdelniku() { // Vně třídy Obdelnik je možný jen přístup Obdelnik.kolikObdelniku(). return pocetObdelniku; Obdelnik obd1 = new Obdelnik(5, 3); Obdelnik obd2 = new Obdelnik(1, 2); Obdelnik obd3 = new Obdelnik(4, 6); System.out.println("Bylo vytvořeno " + kolikObdelniku() + " obdélníků.");
77
Inicializace proměnných třídy
Proměnné třídy se inicializují vždy znovu při vytvoření objektu této třídy. V případě, že inicializace je výpočetně náročná, lze využít způsob zvaný statický inicializační blok. Statický inicializační blok se provede při prvním použití třídy natažené do paměti a verifikované. public class Prvocisla { public static final int MAX = 1000; public static final int cisla[] = new int[MAX]; static { /* začátek statického inicializačního bloku */ int pocet = 2; cisla[0] = 1; cisla[1] = 2; dalsi: for (int i = 3; pocet < MAX; i += 2) { // procházení možnými prvočísly for (int j = 2; j < pocet; j++) { // procházení možnými děliteli prvočísla, if (i % cisla[j] == 0) { // kterými jsou dosud nalezená prvočísla continue dalsi; } cisla[pocet] = i; pocet++; } /* konec statického inicializačního bloku */ public static void main(String[] args) { for (int i = 0; i < Prvocisla.cisla.length; i++) { System.out.print(cisla[i] + " ");
78
Rušení objektů Java má mechanismus pro automatické odstraňování nepotřebných objektů z paměti zvaný garbage collector. Jak se pozná nepotřebný objekt? Není na něj žádný odkaz z existujících referenčních proměnných. Odkaz musí zrušit programátor sám: přiřazením odkazu na jiný objekt Obdelnik obd = new Obdelnik(5, 3); obd = new Obdelnik(10, 4); vynulováním referenční proměnné obd = null; Původní obdélník 5 x 3 v paměti zůstává, dokud jej nezruší garbage collector. Garbage collector běží na pozadí programu s nízkou prioritou. Garbage collector se spustí s vysokou prioritou, když dojde k nedostatku paměti, vyvoláme jej příkazem System.gc(); Záleží na JVM, zda se příkaz ihned nebo vůbec provede. Je vhodné jej spustit před částí programu, která bude mít velké paměťové nároky, jinak radši ne, protože je výpočetně náročný.
79
Ukončení práce s objekty
Před zrušením objektu je někdy třeba provést nějaké akce. Tyto akce může provést metoda instance zvaná Finalizer s hlavičkou protected void finalize() throws Throwable { Jako poslední příkaz této metody by mělo být uvedeno super.finalize(); Kdy je potřeba finalizer naprogramovat? Objekt má ještě jiné zdroje než jen přidělenou paměť, typicky jsou to otevřené soubory. JVM spouští finalizery uvolněných objektů tehdy, když se mu to hodí. Požádat (nemusí být provedeno) o spuštění finalizerů můžeme metodou System.runFinalization(); Místo finalizerů můžeme použít konstrukci try – finally. Finalizery se mohou používat jen, když jejich činnost není nezbytně nutné provést v určitou dobu. Snížení počtu objektů při zániku objektu public class Obdelnik { /* Proměnné, konstruktory a metody vynechány. */ pocetObdelniku--; System.out.println("Konec obdélníku"); } public class Hlavni { public static void main(String[] args) { Obdelnik obd = new Obdelnik(5, 3); Obdelnik obd = new Obdelnik(1, 2); Obdelnik obd = new Obdelnik(4, 6); obd = null; System.runFinalization(); System.gc(); System.out.println("Existuje " + pocetObdelniku + " obdélníků.");
80
Řetězce a znaky Řetězec je v Javě samostatný objekt.
instance třídy String Nepracuje se s ním jako s polem znaků. Nemá ukončovací znak. Jednou vytvořený řetězec již nelze měnit. Potřebujeme-li to, použijeme objekt typu StringBuffer. Instance třídy String je oproti němu rychlejší a paměťově úspornější. Java hlídá překročení mezí řetězců. výjimka StringIndexOutOfBoundsException První index má hodnotu 0. Poslední index má hodnotu o jednu menší než vrací metoda length(). U polí to je proměnná length.
81
Vytvoření řetězce nejefektivněji pomocí konstruktorů
String s = "ahoj"; pomocí konstruktorů řetězec znaků (Unicode) String s1 = new String("ahoj"); char[] znaky = { 'a', 'h', 'o', 'j' }; String s2 = new String(znaky); // s2 = "ahoj" String s3 = new String(znaky, 1, 2); // s3 = "ho" řetězec bajtů char[] znaky = { (byte)'a', (byte)'h', (byte)'o', (byte)'j' }; Bajty se považují za znaky v osmibitovém kódování a to, jak se převedou na 16bitové znaky, záleží na přednastaveném kódování znaků. měnitelný řetězec StringBuffer buf = new StringBuffer("ahoj"); String s4 = new String(buf); inicializované pole řetězců String[] pole = { "Dana", "Eva", "Martina" }; for (int i = 0; i < pole.length; i++) System.out.println(pole[i]);
82
Práce s celými řetězci porovnávání převody na malá či velká písmena
lexikografické Výsledkem je informace o pořadí řetězců. s1.compareTo(s2), s1.compareToIgnoreCase(s2) Vrací int < 0, pokud je s2 větší než s1. Vrací int = 0, pokud se s2 shoduje s s1. Vrací int > 0, pokud je s2 menší než s1. identity Výsledkem je informace mají-li řetězce stejnou posloupnost znaků. s1.equals(s2), s1.equalsIgnoreCase(s2) Vrací true v případě shody s1 a s2, jinak false. operátor == Zjišťuje, zda obě referenční proměnné ukazují na tentýž objekt v paměti, tedy může mít výsledek false u řetězců se stejným obsahem. převody na malá či velká písmena akcentované znaky s1 = s2.toLowerCase(); s2 = s1.toUpperCase(); spojení řetězců s3 = s1 + s2; nebo s3 = s1.concat(s2); náhrada všech znaků v řetězci String s2, s1 = "cacao"; s2 = s1.replace('c', 'k'); // s2 = "kakao"
83
Práce s částí řetězce Získání části řetězce
String s2, s3, s1 = "mala a VELKA"; s2 = s1.substring(5); // s2 = "a VELKA" s3 = s1.substring(5, 9); // s3 = "a VE" char[] znaky = new char[10]; s1.getChars(2, 9, znaky, 0); // znaky = "la a VE" Práce se začátkem a koncem řetězce String krajRetezce = "mala"; if (s.startsWith(krajRetezce)) // if (s.startsWith(krajRetezce) == true) System.out.println("Zacina na \"" + krajRetezce + "\""); if (!s.endsWith(krajRetezce)) // if (s.endsWith(krajRetezce) == false) System.out.println("Nekonci na \"" + krajRetezce + "\""); Oříznutí bílých znaků na okrajích Při načtení řetězce ze vstupu jsou na jeho konci znaky odřádkování, kterých se potřebujeme zbavit. String s2, s1 = "\r\n\t ahoj\t \r\n"; s2 = s1.trim(); // s2 = "ahoj"
84
Práce s jednotlivými znaky řetězce
Získání jednotlivého znaku řetězce s.charAt(indexZnaku); Hledání znaku Není-li znak nalezen, vrací všechny metody hodnotu „-1“, je-li nalezen, je vrácen jeho index. int i = s.indexOf('a'); // index prvního znaku 'a' i = s.indexOf('a', i + 1); // index dalšího znaku 'a' i = s.lastIndexOf('a'); // index posledního znaku 'a' i = s.lastIndexOf('a', i - 1); // index předposledního 'a' Hledání podřetězce v řetězci String s = "mala a VELKA"; i = s.indexOf("VEL"); System.out.println("Prvni VEL je na " + i + ". pozici.");
85
Konverze základních datových typů na řetězec
Tuto akci potřebujeme provést nejčastěji před tiskem hodnoty příslušného datového typu. Používá se metoda String.valueOf nebo operátor +, před kterým musí být String. Metoda System.out.println() volá metodu String.valueOf na své parametry automaticky. Metodu String.valueOf musíme použít, když chceme například oříznout počet míst. String s = String.valueOf(Math.PI); // nebo String s = "" + Math.PI; System.out.println(s.substring(0, s.indexOf('.') + 6)); // Výpis hodnoty v jiné číselné soustavě Datový typ Integer a Long má metody toBinaryString(), toOctalString(), toHexString() a toString(int/long i, int radix). int i = 254; s = Integer.toBinaryString(i); Tisk do řetězce například generování názvů souborů for (int i = 1; i <= 567; i++) { System.out.println(String.format("PIC-%04d.jpg", i)); }
86
Konverze řetězce na základní datové typy
Načítáme-li čísla z textových souborů nebo z prvků GUI, dostáváme řetězec, jehož obsah je nutné převést na číslo v odpovídajícím datovém typu. Pro převod se používá statická metoda valueOf() tříd Boolean, Byte, Short, Integer, Long, Float a Double z balíku java.lang, která vrací řetězec zkonvertovaný na objekt třídy příslušného datového typu. Tento objekt je pak nutné převést na odpovídající základní datový typ některou z metod datovýtypValue(). double d1 = Double.valueOf("3.14").doubleValue(); double d2 = new Double("3.14").doubleValue(); // tvorba pomocí konstruktoru Celočíselné typy Byte, Short, Integer a Long mají statickou metodu valueOf() přetíženou, takže jako druhý parametr lze zadat základ číselné soustavy, ze které se má převádět. int i = Integer.valueOf("1A2B", 16).intValue(); Obalovací třídy nabízejí ještě jeden způsob konverze, a to pomocí statické metody parseDatovýtyp(). Tyto metody jsou pro celočíselné typy opět přetíženy, takže lze převádět i z jiných soustav než z desítkové. Pro reálné typy lze opět samozřejmě použít i zápis pomocí vědecké notace. int j = Integer.parseInt("12345"); int i = Integer.parseInt("1A2B", 16); float f = Float.parseFloat("1.235e2"); Obecně je výhodnější používat pro převod řetězce metodu parseDatovýtyp(), protože při ní nevzniká nový objekt. Převod je tak asi až o 15 až 20 % rychlejší než při použití metody valueOf().
87
Vyvolání více metod jedním příkazem
Množství metod třídy String vrací objekt typu String, což prakticky znamená, že vytvoří nový řetězec. Odkaz na tento řetězec není nutné ukládat do pomocné referenční proměnné a pak s ní dále pracovat, ale můžeme volání těchto metod spojit (zřetězit) za sebe. String s1 = "\r\n\t ahoj\t \r\n"; int i = s1.trim().toUpperCase().substring(2).indexOf('O'); System.out.println("O je " + (i + 1) + ". znak. "); Zde se postupně tvoří řetězce "ahoj", "AHOJ", "OJ". Všechny tyto řetězce jsou po použití zrušeny pomocí garbage collectoru. Podobně lze jakoukoliv z výše zmíněných metod zavolat jako metodu konstantního řetězce, např.: String s1 = "\r\n\t ahoj\t \r\n".trim().toUpperCase().substring(2); System.out.println(s1); String s2 = "obr".concat(String.valueOf(i)).concat(".jpg"); nebo čitelněji String s2 = "obr" + i + ".jpg";
88
Dělení řetězce na části pomocí split()
Z řetězce potřebujeme dostat hodnoty oddělené určitými znaky. Rozdělení řetězce na podřetězce se nazývá parsování (parsing). Dříve se pro tuto činnost používala třída java.util.StringTokenizer. Od JDK 1.4 je žádoucí místo toho používat metodu split() třídy String. Základní použití String radka = ",;123;;;,,,45;6;,;789;;,,,;;"; String[] podretezce = radka.split(";"); for (int i = 0; i < podretezce.length; i++) { System.out.println("|" + podretezce[i] + "|"); } // Proměnná podretezce je pole řetězců.
89
Regulární výrazy Umožňují složitější definování oddělujících znaků.
java.util.regex.Pattern Využívají se i pro vyhledávání vzorů v řetězci. metoda String.matches() Metoda split() neodstraňovala oddělovače z počátku řetězce. To lze opravit: String radka = ",;123;;;abc,,,45;6;,;789;;,,,;;"; String[] podretezce = ",".concat(radka).split("([;, ]|\\Qabc\\E)+"); Je vhodné využít POSIX character class "(\\p{Punct}|\\Qabc\\E)+" for (int i = 1; i < podretezce.length; i++) { System.out.println("|" + podretezce[i] + "|"); } Je-li na začátku jeden nebo více oddělovačů, tak je prvním prvkem pole podretezce jeden prázdný řetězec. Tak se přidá jeden oddělovač před řetězec radka, aby to bylo jednotné v případech, kdy je nebo není oddělovač před začátkem prvního prvku, a pole podretezce se bude vypisovat od 2. prvku.
90
Metoda toString() Je deklarována v kořenové třídě Object.
Pokud ji ve své třídě nepřekryjete a přesto použijete, dostanete řetězec, který je složen ze jména vaší třídy, oddělovacího a jednoznačné identifikace objektu. System.out.println(podretezce.toString()); Účelem metody toString() je poskytnout znakovou reprezentaci objektu, kterou lze přímo tisknout nebo zapisovat do logovacích souborů a podobně. Je důrazně doporučováno ve své třídě metodu toString() překrýt, aby se vypisovalo to, co programátor chce.
91
Překrytí metody toString()
import java.util.*; public class MujString { String[] hodnota; MujString(String[] s) { hodnota = s; } public String toString() { // public je nutný, protože rodičovská třída má také public. String jmenoTridy = new String(getClass().getName()); return (jmenoTridy + ": " + Arrays.toString(hodnota)); } void puvodniToString() { System.out.println(super.toString()); } // Pomocí super se volá původní metoda toString ze třídy Object. public class Main { public static void main(String[] args) { String radka = "123;;;,,,45;6;,;789;;,,,;;"; String[] podretezce = radka.split("\\p{Punct}+"); MujString podretezec = new MujString(podretezce); podretezec.puvodniToString(); System.out.println(podretezec.toString());
92
Třída StringBuffer Poskytuje typ „měnitelný řetězec“.
K dispozici jsou tři konstruktory: StringBuffer b1 = new StringBuffer(); 16znakový řetězec s hodnotu znaků \u0000 StringBuffer b2 = new StringBuffer(100); 100znakový řetězec s hodnotou znaků \u0000 StringBuffer b3 = new StringBuffer("Ahoj"); První 4 znaky jsou pro Ahoj a dalších 16 znaků \u0000 je jako rezerva.
93
Délka řetězce třídy StringBuffer
Aktuální délka se zjistí pomocí metody length(). Kapacita se zjistí pomocí metody capacity(). Kapacitu je možné změnit dvěma metodami: ensureCapacity(int k) Když je k > než současná kapacita, tak bude nová kapacita rovna maximu z k a z dvojnásobku současné kapacity + 2 znaky navíc. Když je k <= současné kapacitě, ponechá řetězec nezměněn. b2.ensureCapacity(110); System.out.println(b2.capacity()); // 202 setLength(int k) Když je k > než současná kapacita, tak zvětší aktuální délku řetězce na k a kapacitu na dvojnásobek současné kapacity + 2 znaky navíc. Když je k <= současné kapacitě, ponechá kapacitu nezměněnu a aktuální délku nastaví na k – může řetězec prodloužit i oříznout. b1.setLength(18); System.out.println(b1.capacity()); // 34 b3.setLength(3); System.out.println(b3.capacity()); // 20
94
Změny řetězce třídy StringBuffer
StringBuffer b = new StringBuffer("Ahoj"); Změny celého řetězce Metodou reverse() lze celý řetězec obrátit. b.reverse(); // johA Změny části řetězce Metodou append(typ t) lze přidat na konec řetězce libovolný základní datový typ. b.append(true); // johAtrue b.append(3); // johAtrue3 Metodou delete(int počátečníIndex, int koncovýIndex) lze vyříznout libovolnou část. b.delete(4, 8); // johA3 Metodou deleteCharAt(int index) lze po jednotlivých znacích ubírat. b.deleteCharAt(1); // jhA3 Metodou insert(int index, typ t) lze do řetězce vkládat libovolný datový typ. b.insert(0, 3.14).insert(1, "AHOJ"); // 3AHOJ.14jhA3 Metodou replace(int počátečníIndex, int koncovýIndex, String novýPodřetězec) nahradíme jeden podřetězec jiným. b.replace(0, 6, "3,"); // 3,14jhA3 Metodou charAt(int index) získáme jednotlivé znaky. char c = b.charAt(6); // c = 'A' Metodou setCharAt(int index, char ch) změníme určitý znak. b.setCharAt(1, '!'); // 3!14jhA3 Nahrazení všech výskytů podřetězce v řetězci umí řetězec třídy String: String s = new StringBuffer(b); s = s.replace("3", "AHOJ"); // AHOJ!14jhAAHOJ
95
Konverze typu StringBuffer na String a StringBuilder
StringBuffer b = new StringBuffer("Ahoj"); String s1 = b.toString(); // Ahoj String s2 = b.substring(1); // hoj String s3 = b.substring(1, 3); // oj Existuje ještě třída StringBuilder mající stejné metody jako třída StringBuffer. Třídu StringBuffer je nutné využívat pro vícevláknové aplikace, protože synchronizuje. Třída StringBuilder je efektivnější, protože nesynchronizuje. Řetězec třídy String lze změnit, ale výsledný řetězec je jiný objekt a při jeho tvorbě byl dočasně vytvořen objekt třídy StringBuffer.
96
Třída Character Ve třídě java.lang.Character je mimo jiné několik metod třídy užitečných jak pro rozpoznávání druhu znaku, tak i pro změnu jednotlivých znaků. Tyto metody jsou zhruba ekvivalenty maker z <ctype.h>. Mají 2 varianty: pro typ char a int. Rozpoznávání druhů znaků public static boolean isDruhZnaku(char ch) isDigit(), isLetter(), isLetterOrDigit(), isLowerCase(), isUpperCase() a isWhiteSpace() Změna velikosti písmene public static char toLowerCase(char ch) public static char toUpperCase(char ch) Převod jednotlivých znaků na čísla Může se využít pro kontrolu vstupu. public static int digit(char ch, int základ) System.out.println(Character.digit('F', 16)); // 15
97
Struktura projektu v jazyce Java
Každý objekt neprimitivního typu má vlastní třídu uloženou v samostatném souboru. Hlavním programem je třída, která obsahuje pouze metodu main(). Deklaruje referenční proměnné na ostatní třídy. Volá pomocí nich jejich metody. Překlad všech souborů *.java v adresáři najednou javac *.java Spuštění programu java Hlavni Kde Hlavni je třída obsahující metodu main().
98
Modifikátory deklarace třídy
Vyskytují se před klíčovým slovem class. bez modifikátoru Třída je přístupná pouze ve svém balíku. public – veřejná třída Třída je přístupná i mimo balík, kde je deklarována. Třída musí být uložena v samostatném stejnojmenném souboru. abstract – abstraktní třída Od této třídy nelze vytvořit instanci. Třída tvoří společný základ pro více tříd, které budou od ní odděděny. final – koncová třída (opak abstraktní třídy) Od této třídy se tvoří pouze instance. Nesmí být použita jako rodičovská třída pro přípravu jiných tříd. Je efektivnější, může se proto použít pro struktury. kombinace modifikátorů přípustné: public abstract, public final nepřípustné: final abstract
99
Kompozice objektů Třída může mít členskou proměnnou objektového typu.
Spojový seznam má členskou referenční proměnnou téže třídy. Například objekt třídy Zamestnanec může mít členskou proměnnou třídy Datum. class Zamestnanec { public String jmeno; public Datum narozeni, nastup; public Zamestnanec(String jmeno, Datum narozeni, Datum nastup) { this.jmeno = new String(jmeno); this.narozeni = new Datum(narozeni); this.nastup = new Datum(nastup); } public class Kompozice { public static void main(String[] args) { Datum narozeni = new Datum(21, 5, 1960); Zamestnanec z = new Zamestnanec("Josef Novák", narozeni, new Datum(1, 10, 1990)); System.out.println(z.toString());
100
Autorizovaný přístup k datům
Je důsledkem zapouzdření dat (data encapsulation). S daty třídy nemá být možné manipulovat z vnějšku třídy jinak než pomocí metod této třídy. Proměnné deklarujeme s přístupovým právem private místo public. Jejich hodnoty zpřístupňujeme zvenčí pomocí public metod. Tyto metody typicky provádějí nastavení hodnoty (setProměnná) a zjištění hodnoty (getProměnná). Vývojová prostředí vytvářejí tyto metody automaticky na základě deklarovaných proměnných a generují názvy začínající na set a get. I v ručně vytvářených metodách se doporučuje tyto předpony užívat. public metody mohou volat private metody. Konstruktory musí být public. Výjimkou může být singleton.
101
Pole objektů Vytvoříme třídu pro prvek pole. Ve třídě pro pole objektů
alokujeme pole referenčních proměnných, Obdelnik[] poleObdelniku = new Obdelnik[10]; v cyklu vytvoříme prvky pole podle jejich třídy. for (int j = 0; j < poleObdelniku.length; j++) { poleObdelniku[j] = new Obdelnik(j, 2 * j); } Pole objektů obalovacích tříd Od JDK 1.5 můžeme používat automatické převody mezi primitivními a obalovými typy. Integer[] pole = new Integer[10]; // pole objektů pole[i] = new Integer(i); // objekt Integer int i = pole[i].intValue(); // explicitní převod na int int i = pole[i]; // automatický převod na int pole[i] = i; // automatický převod na Integer
102
Předávání skutečných parametrů metodám
Předávání primitivních datových typů Pouze hodnotou (call by value) Výhody Skutečným parametrem může být i konstanta nebo výraz. Skutečný parametr nelze omylem v metodě změnit, protože se pracuje s jeho kopií. Nevýhody Skutečný parametr nelze v metodě změnit, i když si to přejeme. Metody třídy a instance mění parametry. Předávání objektů Objekty se metodám předávají přes kopii referenční proměnné, pomocí níž lze přistupovat k metodám a datovým prvkům daného objektu. Předávání jednorozměrných polí Pole se metodám předávají stejně jako objekty, takže se jejich prvky trvale mění. Je-li pole složeno pouze z primitivních datových typů a předáváme-li jediný prvek, je předáván hodnotu – nezmění se. Předávání vícerozměrných polí Ve formálním parametru metody se musí pomocí prázdných složených závorek udat, kolikarozměrné pole bude.
103
Dědičnost Dědičnost představuje možnost přidat k základní třídě (též bázové, rodičovské, supertřídě nebo rodiči případně předkovi) další vlastnosti a vytvořit tak odvozenou třídu (zděděnou třídu nebo potomka či dceřinou třídu). Odvozená třída bývá specializovanější než třída základní. Kromě ní lze využívat kompozici. Pro rozhodnutí, zda je vhodnější kompozice nebo dědění se používá „Je test“ nebo „Má test“. Je datum nástupu zaměstnancem? Ne, a proto použijeme kompozici. Má zaměstnanec datum nástupu? Ano, proto použijeme kompozici. Je vrátný zaměstnancem? Ano, a proto použijeme dědičnost.
104
Realizace dědičnosti Třída Kvadr dědí ze třídy Obdelnik.
public class Kvadr extends Obdelnik { public int hloubka; public Kvadr(int sirka, int vyska, int hloubka) { super(sirka, vyska); // volání konstruktoru třídy Obdelnik this.hloubka = hloubka; } public double delkaUhlopricky() { double pom = super.delkaUhlopricky(); pom = (pom * pom) + (hloubka * hloubka); return Math.sqrt(pom); Metoda delkaUhlopricky() ze třídy Obdelnik je překryta (overriding). Má stejný identifikátor i formální parametry (zde žádné). Statické metody jsou tímto způsobem zastíněny (hiding). Překrytí proměnné Chceme proměnnou stejného jména ale jiného typu. Specializovaná referenční proměnná ve třídě potomka překrývá obecnější referenční proměnnou rodičovské třídy. Přístup k překrytým a skrytým metodám a proměnným V metodách instance se použije klíčové slovo super. V metodách třídy (statických metodách) se využije plně kvalifikované jméno.
105
Konstruktory rodičovské třídy
Během konstrukce objektu typu potomka musí být vždy dána možnost, aby byl vyvolán konstruktor rodiče. Mohou nastat dva případy: V rodiči je konstruktor bez parametrů nebo implicitní. Konstruktor v potomkovi může být implicitní. V rodiči je konstruktor alespoň s jedním parametrem. Konstruktor potomka musí existovat a jako svůj první příkaz musí volat pomocí super() konstruktor rodiče. Pokud potomek zastiňuje metodu předka, často na začátku volá pomocí super. metodu předka, ke které pak přidá další funkcionalitu. Pomocí super. se lze dostat jen o jednu úroveň výše. Nelze tedy volat metodu „dědečka“ pomocí super.super.metodaDedecka(). Pokud neexistuje nebo nevolá konstruktor rodiče, je hlášena chyba: constructor Rodic() not found in class Rodic Je velmi vhodné připravit v rodičovské třídě i jeden konstruktor bez parametrů, aby se zjednodušili potomci.
106
Finální a abstraktní metody a třídy
finální metody – final metody, u kterých chceme zabránit jejich překrytí ve zděděných třídách abstraktní metody a třídy – abstract Abstraktní metoda má uveden návratový typ, jméno a formální parametry, ale ne tělo. abstract int getI(); Abstraktní metody mají vynucené naprogramování (překrytí) ve zděděné třídě. Jakmile má třída jednu abstraktní metodu, nelze podle ní vytvořit instanci třídy a z toho důvodu musí být označena jako celá abstraktní. Třída java.lang.ClassLoader je abstraktní třída bez abstraktních metod, která umožňuje dynamické sestavování programu. JVM má rozeznat, které třídy natáhl sám svojí zděděnou třídou a které natáhl pomocí zděděné třídy uživatele. Finální třídy třídy, které se nedají dědit, nemohou mít potomky Jsou optimalizovány při překladu, proto je jich mnoho v Java Core API.
107
Třída Object Každá třída v jazyce Java má jen jedinou rodičovskou třídu. Vícenásobnou dědičnost z jazyka C++ nahrazuje mechanismus rozhraní. Nejvyšší v hierarchii je třída java.lang.Object. Definuje základní stav a chování všech objektů. Všechny třídy (kromě zděděných) by bylo možné napsat jako class MojeTrida extends Object { Dědění od objektu Object je však automatické. Nemá žádné z vnějšku přístupné datové prvky. Má 9 metod.
108
Metody třídy Object Překrýt lze metody
clone() klonování, vytváření zcela identických kopií jedné instance pro objekty vzniklé kompozicí a děděním je bezpečnější než pomocí konstruktoru s inicializací jiným objektem. equals() Porovnává reference na objekty namísto obsahů objektů. Ve třídě String je metoda equals(), která ji překrývá, porovnává obsah řetězců. hashCode() Vrací pro každý objekt číslo typu int, které se nemění při změně stavu objektu. finalize() Voláním System.runFinalization() je možné požádat o vyvolání metod finalize() všech instancí, které zanikly a dosud jejich metody finalize() neproběhly. toString() Finální (nepřekrytelné) jsou metody getClass() Vrací objekt třídy Class, kdy třída Class je přímý potomek třídy Object. Instance Class zahrnují vše, co se v programu objeví od primitivních datových typů až po pole. notify(), notifyAll, wait() vícevláknové aplikace
109
Balíky Jsou knihovnami tříd.
Tím, že jsou třídy uloženy v konkrétním balíku, máme možnost lehce určit, které třídy a rozhraní k sobě patří, zabránit konfliktům v pojmenování tříd, omezit přístupová práva ke třídám, metodám a proměnným. Každý balík je obvykle reprezentován adresářem, který obsahuje přeložené zdrojové soubory (.class). Vnořování adresářů vzniká hierarchie balíků. Jména adresářů odpovídají jménům balíků. Cestu k balíkům udává systémová proměnná CLASSPATH, která standardně obsahuje cestu k balíkům Java Core API a do aktuálního adresáře. Pro přístup do balíků se používá tečková notace java.lang.Math.PI // přístup ke statické konstantě PI ve třídě Math import balíků import java.util.Scanner; Příkaz musí končit celým jménem třídy nebo balíkem, za kterým je „.*“. Celosvětově platná konvence pro pojmenování doménové jméno na Internetu zapsané pozpátku následované jménem autora a balíku package cz.zcu.kiv.herout.editor
110
Import balíků V každém zdrojovém textů programu je vždy implicitně proveden příkaz import java.lang.* Tím se importují tři balíky java.lang default package balík, ve kterém jsou všechny třídy, které nepatří do žádného balíku (nemají na svém začátku uvedeno klíčové slovo package) aktuální balík ten, který má shodné jméno se jménem, které je uvedeno za klíčovým slovem package na začátku příslušné třídy. Za účelem umožnění optimalizace při spouštění programu či vyhnutí se kolizím stejných jmen tříd v různých balících se často importují jen ty třídy, které ve svém programu skutečně použijeme. Je to podporováno různými vývojovými prostředími například Eclipse a NetBeans. Místo jednoho řádků importu je potom řádek více.
111
Statický import balíků
Je k dispozici od JDK 1.5. Bez statických importů musíme například metodu sin() a konstantu PI použít ve spojení se jménem třídy Math. double d = Math.sin(Math.Pi / 2); S použitím statického importu import static java.lang.Math.*; double d = sin(Pi / 2); Je možné staticky importovat jen jednotlivé metody či konstanty. import static java.lang.Math.sin; Může zpřehlednit program, ale nadužívání naopak způsobí kolize mezi vlastními a knihovními jmény a nebude jasné, odkud identifikátory pocházejí.
112
Přístupová práva Pomocí balíků lze kontrolovat přístupová práva.
Před každou třídou, proměnnou, konstantou a metodou lze uvést tři možná přístupová práva. private třída Lze uvést pouze před vnořenou třídou. proměnná Pro zajištění autorizovaného přístupu k datům by měly být takto označeny všechny proměnné. metoda Pouze pomocné metody volané veřejnými metodami. protected Je méně restriktivní než private. Je možný přístup z jakékoliv odvozené třídy a z libovolné třídy v tomtéž balíku. Rozdíl oproti neuvedení žádného modifikátoru je, že protected dovoluje dědit atributy třídy i třídám z jiného balíku. proměnná a metoda když nemá být vidět z vnějšku, ale má být použitelná v odvozené třídě public třída přístupná i mimo svůj balík, která musí být v samostatném souboru Z hlediska autorizovaného přístupu k datům je to nebezpečné. metoda volně přístupná komukoliv Specifikátor neuveden (je „přátelský“) pro pomocné třídy proměnné a metody, které nebudou děděny v jiném balíku
113
Přístupová práva a dědění
Java nepovoluje zeslabit (tj. omezit) ve zděděné třídě přístupová práva. Neplatí to na úrovni tříd. Třída public může být zděděna třídou bez označení a naopak. Pokud autor třídy dal něco k dispozici všem, nemůže jiný autor využitím pouhého dědění změnit rozhodnutí původního autora. rodič potomek private neuvedeno protected public ano ne
114
Rozhraní (interface) Původně bylo částečnou náhradou za vícenásobnou dědičnost z jazyka C++. Dnes silná stránka Javy nahrazující například abstraktní třídy. Rozhraní definuje soubor metod, které ale v něm nejsou implementovány, tj. v deklaraci rozhraní je pouze hlavička metody, stejně jako je to u abstraktní metody. Rozhraní se dá považovat za druh třídy, která má ve své hlavičce místo slova class slovo interface. Třída, která toto rozhraní implementuje (tj. jakoby zdědí), musí překrýt (tedy implementovat) všechny jeho metody. Rozhraní není totéž co abstraktní třída, protože rozhraní nemůže deklarovat žádné proměnné, jen konstanty, třída může implementovat více než jedno rozhraní, rozhraní je nezávislé na dědičné hierarchii tříd, tj. naprosto odlišné třídy mohou implementovat stejné rozhraní. Použití rozhraní je výhodné v případech, kdy chceme třídě vnutit zcela konkrétní metody, vidíme podobnosti v různých třídách, ale začlenit tyto podobnosti do vlastností tříd pomocí dědění by vyžadovalo zkonstruovat jejich předka. Pokud třídy vznikly děděním knihovních tříd, tak to není možné. Společný předek našich tříd by byl vykonstruovaný. I velmi odlišné objekty mají spoustu vlastností stejnou (hmotnost, porovnatelnost).
115
Konstrukce a použití rozhraní
public interface Info { // rozhraní public void kdoJsem(); // public není nutné, všechny metody rozhraní jsou implicitně public. } public class Usecka implements Info { int delka; Usecka(int delka) { this.delka = delka; } public void kdoJsem() { System.out.println("Usecka"); // implementace public class Koule implements Info { int polomer; Koule(int polomer) { this.polomer = polomer; } System.out.println("Koule"); // implementace public class Test { public static void main(String[] args) { Usecka u = new Usecka(5); Koule k = new Koule(3); u.kdoJsem(); // Vypíše Usecka. k.kdoJsem(); // Vypíše Koule. Pokud bychom zapomněli napsat implementaci, překladač by hlásil chybu: class Koule should be declared abstract; it does not define method kdoJsem() in interface Info.
116
Použití rozhraní jako typu referenční proměnné
Pokud je rozhraní nějakou třídou implementováno, lze deklarovat referenční proměnnou typu rozhraní, pomocí které lze pak přistupovat k instancím všech tříd, které toto rozhraní implementují. Případně lze vytvořit novou instanci třídy a přiřadit ji proměnné typu rozhraní. public class Test { public static void main(String[] args) { Info i = new Usecka(5); Koule k = new Koule(3); i.kdoJsem(); // Vypíše Usecka. i = k; i.kdoJsem(); // Vypíše Koule. }
117
Implementace více rozhraní jednou třídou
public interface InfoDalsi { // další rozhraní přidané k předchozímu příkladu public void vlastnosti(); } public class Usecka implements Info, InfoDalsi { int delka; Usecka(int delka) { this.delka = delka; } public void kdoJsem() { System.out.println("Usecka"); // implementace public void vlastnosti() { System.out.println(" = " + delka); // implementace public int getDelka() { return delka; } public class Test { public static void main(String[] args) { Usecka u = new Usecka(5); u.kdoJsem(); u.vlastnosti(); // Vypíše Usecka = 5.
118
Instance rozhraní může využívat jen metody rozhraní.
public class Test { // třída Usecka je stejná. public static void main(String[] args) { Info info = new Usecka(5); info.kdoJsem(); // Vypíše Usecka. // info.vlastnosti(); // chyba // System.out.println(info.getDelka()); // chyba }
119
Implementované rozhraní se dědí beze změny.
class Usecka implements Info { int delka; Usecka(int delka) { this.delka = delka; } public void kdoJsem() { System.out.println("Usecka"); // implementace } class Obdelnik extends Usecka { int sirka; Obdelnik(int delka, int sirka) { super(delka); this.sirka = sirka; System.out.println("Obdelnik"); // implementace v potomkovi public class Test { public static void main(String[] args) { Usecka u = new Usecka(5); Obdelnik o = new Obdelnik(2, 4); Info iu = new Usecka(6); Info io = new Obdelnik(3, 6); u.kdoJsem(); // Vypíše Usecka. // Bez implementace v potomkovi to samé vypíše i o.kdoJsem(); iu.kdoJsem(); io.kdoJsem();
120
Dědění třídy a současná implementace rozhraní
class Usecka implements Info { int delka; Usecka(int delka) { this.delka = delka; } public void kdoJsem() { System.out.println("Usecka"); // implementace } class Obdelnik extends Usecka implements InfoDalsi { int sirka; Obdelnik(int delka, int sirka) { super(delka); this.sirka = sirka; System.out.println("Obdelnik"); // implementace rozhraní Info v potomkovi public void vlastnosti() { System.out.println(" = " + delka + ", " + sirka); // implementace rozhraní InfoDalsi v potomkovi public void vypisSirka() { System.out.println(sirka); // není z žádného rozhraní public class Test { public static void main(String[] args) { Obdelnik o = new Obdelnik(2, 4); Info io = new Obdelnik(3, 6); InfoDalsi iod = new Obdelnik(5, 7); // InfoDalsi iud = new Usecka(6); // Chyba: Třída Usecka neimplementuje rozhraní InfoDalsi. o.kdoJsem(); o.vlastnosti(); // Vypíše Obdelnik = 2, 4. io.kdoJsem(); ((Obdelnik)io).vlastnosti(); // Vypíše Obdelnik = 3, // Pomocí přetypování lze získat z referenční proměnné typu rozhraní ((Obdelnik)iod).kdoJsem(); iod.vlastnosti(); // Vypíše Obdelnik = 5, 7. // přístup k metodám třídy, které by jinak přístupné nebyly. ((Obdelnik)io).vypisSirka(); // Vypíše 6.
121
Dědění rozhraní a konstanty rozhraní
public interface InfoOba extends Info, InfoDalsi { public static final int POCET = 3; } class Usecka implements Info { int delka; Usecka(int delka) { this.delka = delka; } public void kdoJsem() { System.out.println("Usecka"); // implementace class Obdelnik extends Usecka implements InfoOba { int sirka; Obdelnik(int delka, int sirka) { super(delka); this.sirka = sirka; System.out.println(POCET + "Obdelnik"); // implementace rozhraní InfoOba v potomkovi public void vlastnosti() { System.out.println(" = " + delka + ", " + sirka); // implementace rozhraní InfoOba v potomkovi public class Test { public static void main(String[] args) { Info i = new Obdelnik(3, 6); InfoDalsi id = new Obdelnik(5, 7); InfoOba io = new Obdelnik(2, 4); i.kdoJsem(); ((Obdelnik)i).vlastnosti(); // Vypíše 3 Obdelnik = 3, 6. ((Obdelnik)id).kdoJsem(); id.vlastnosti(); // Vypíše 3 Obdelnik = 5, 7. io.kdoJsem(); io.vlastnosti() // Vypíše 3 Obdelnik = 2, 4. System.out.println("Počet rozhraní = " + InfoOba.POCET); // Vypíše Počet rozhraní = 3.
122
Využití operátoru instanceof
Za běhu programu lze poznat, zda třída implementuje určité rozhraní. Pomocí testu (proměnná instanceof třída) můžeme vytvářet programy využívající metody, které ještě nebyly implementovány, nenastane chyba při překladu, pokud se tyto metody pokusíme vyvolat nad objektem třídy, která dané rozhraní neimplementovala. V příkladu jsou třídy Usecka a Obdelnik stejné jako na předchozím snímku. public class Test { public static void main(String[] args) { Usecka u = new Usecka(5); Obdelnik o = new Obdelnik(3, 6); if (u instanceof Info) System.out.println("u implementuje Info"); // Vypíše se. if (o instanceof Info) System.out.println("o implementuje Info"); // Vypíše se. if (u instanceof InfoOba) System.out.println("u implementuje InfoOba"); // Nevypíše se. if (o instanceof InfoOba) System.out.println("o implementuje InfoOba"); // Vypíše se. if (u instanceof Usecka) System.out.println("u je instanci Usecka"); // Vypíše se. }
123
Polymorfizmus Do referenční proměnné typu předka můžeme přiřadit referenci na instanci potomka. díky automatické rozšiřující konverzi Pak lze přes referenční proměnnou předka využívat i metody potomka. Využití, když je potomků více typů a my pak k nim přistupujeme jednotným způsobem.
124
Polymorfizmus a abstraktní třída
Při konstrukci kořenové třídy již víme, jaké budeme potřebovat metody, ale jejich konkrétní implementace bude silně záviset na povaze zděděných tříd. Vytvoříme abstraktní metodu s úplně definovanými parametry a návratovým typem. Tím donutíme programátory zděděných tříd, aby tuto metodu překryli (přeprogramovali).
125
Polymorfizmus a abstraktní třída na příkladu
abstract class Zivocich { String typ; Zivocich(String typ) { this.typ = new String(typ); } public void vypisInfo() { System.out.print(typ + ", "); vypisDelku(); } @Override public abstract void vypisDelku(); class Ptak extends Zivocich { int delkaKridel; Ptak(String typ, int delka) { super(typ); delkaKridel = delka; public void vypisDelku() { System.out.println("délka křídel: " + delkaKridel); class Slon extends Zivocich { int delkaChobotu; Slon(String typ, int delka) { super(typ); delkaChobotu = delka; } public void vypisDelku() { System.out.println("délka chobotu: " + delkaChobotu); class Had extends Zivocich { int delkaTela; Ptak(String typ, int delka) { delkaTela = delka; System.out.println("délka těla: " + delkaTela);
126
Polymorfizmus a abstraktní třída na příkladu
Jakmile máme tyto třídy, je možné vytvořit pole živočichů a postupně mu náhodně přiřadit objekty tříd Ptak, Slon a Had. Potom je můžeme jednotně vypsat. public class PolymAbstr { public static void main(String[] args) { Zivocich[] z = new Zivocich[6]; for (int i = 0; i < z.length; i++) { switch ((int) (1.0 + Math.random() * 3.0)) { case 1: z[i] = new Ptak("pták", i); break; case 2: z[i] = new Slon("slon", i); break; case 3: z[i] = new Had("had", i); break; } z[i].vypisInfo();
127
Polymorfizmus a neabstraktní třída na příkladu
class Zivocich { public void vypisInfo() { System.out.print(getClass.getName() + ", "); } class Ptak extends Zivocich { int delkaKridel; Ptak(int delka) { delkaKridel = delka; } super.vypisInfo(); System.out.println("délka křídel: " + delkaKridel); class Slon extends Zivocich { int delkaChobotu; Slon(int delka) {delkaChobotu = delka; } public void vypisInfo() { super.vypisInfo(); System.out.println("délka chobotu: " + delkaChobotu); } class Had extends Zivocich { int delkaTela; Had(int delka) {delkaTela = delka; } System.out.println("délka těla: " + delkaTela);
128
Polymorfizmus a neabstraktní třída na příkladu
public class PolymDedeni { public static void main(String[] args) { Zivocich[] z = new Zivocich[6]; for (int i = 0; i < z.length; i++) { switch ((int) (1.0 + Math.random() * 3.0)) { case 1: z[i] = new Ptak(i); break; case 2: z[i] = new Slon(i); break; case 3: z[i] = new Had(i); break; } z[i].vypisInfo();
129
Polymorfizmus a rozhraní
V případě, kdy se společný předek těžko hledá, je možné použít rozhraní. interface Vazitelny { public void vypisHmotnost(); // Bylo by lepší, kdyby zde byla i metoda getHmotnost(). } class Clovek implements Vazitelny { int vaha; String profese; Clovek(String povolani, int tiha) { profese = new String(povolani); vaha = tiha; public void vypisHmotnost() { System.out.println(profese + ": " + vaha); public int getHmotnost() { return vaha; } class Kufr implements Vazitelny { Kufr(int tiha) { vaha = tiha; } System.out.println("kufr: " + vaha);
130
Polymorfizmus a rozhraní
public class PolymRozhrani { public static void main(String[] args) { int vahaLidi = 0; Vazitelny[] kusJakoKus = new Vazitelny[3]; kusJakoKus[0] = new Clovek("programátor", 100); kusJakoKus[1] = new Kufr(20); kusJakoKus[2] = new Clovek("modelka", 51); for (int i = 0; i < kusJakoKus.length; i++) { kusJakoKus[i].vypisHmotnost(); // využití polymorfismu, výpis všeho if (kusJakoKus[i] instanceof Clovek == true) { // vahaLidi += kusJakoKus[i].getHmotnost(); // využití polymorfismu // předchozí řádek by byl možný, kdyby getHmotnost() byla v rozhraní. vahaLidi += ((Clovek) kusJakoKus[i]).getHmotnost(); // přetypování // přetypování je však závislé na typu, proto by byl lepší polymorfismus. } System.out.println("Živá váha: " + vahaLidi);
131
Vnořená třída nested class, top-less class
Prvkem třídy může být i jiná třída (ve speciálních případech i rozhraní). Třída, která obsahuje nějakou vnořenou třídu, se označuje jako třída nejvyšší úrovně (top-level class) nebo z pohledu vnořené třídy jako vnější třída. Má neomezená přístupová práva ke všem proměnným a metodám své vnější třídy. K jejím proměnným a metodám není přístup přes referenční proměnnou na vnější třídu. V následujících příkladech budou probrány pouze vnitřní třídy implementující rozhraní. Využívá se pro adaptéry v grafických prostředích typu AWT či Swing nebo pro třídění. Při překladu vzniknou soubory VnejsiTrida.class VnejsiTrida$VnorenaTrida.class
132
Vnitřní třídy implementující rozhraní
Chceme aby metoda rozhraní byla přístupná pouze přes referenční proměnnou typu rozhraní, přes referenční proměnnou na vnější třídu nebude možné implementovanou metodu rozhraní z této třídy zavolat. K tomu nelze využít přístupové právo private, protože metoda implementující rozhraní musí být přístupná z vnějšku.
133
Příklad vnitřní třídy implementující rozhraní
public interface Info { void kdoJsem(); } class Usecka { int delka; Usecka(int delka) { this.delka = delka; } public Info informace() { return new UseckaInfo(); // Vrací referenci na rozhraní Info class UseckaInfo implements Info { public void kdoJsem() { System.out.println("Usecka" + delka); public class Test { public static void main(String[] args) { Usecka u = new Usecka(5); // u.kdoJsem(); // Nelze, protože třída Usecka neimplementuje rozhraní s metodou kdoJsem(). // Info i = u; // Nelze. Info i = u.informace(); // Vznikne nový objekt třídy UseckaInfo reprezentující rozhraní Info. i.kdoJsem(); // Vypíše Usecka 5. u.informace().kdoJsem(); // Bez pomocné proměnné i vypíše Usecka 5.
134
Implementace rozhraní pomocí metody využívající anonymní vnitřní třídu
V adaptérech při programování obsluh událostí v GUI nás nezajímá jméno vnitřní třídy ale jen její instance. Výsledný kód je mnohem méně čitelný, proto se doporučuje používat jen pro velmi jednoduché rozhraní. Kód s anonymními vnitřními třídami je často generován vývojovými prostředími. Při překladu vzniknou soubory VnejsiTrida.class VnejsiTrida$1.class Kde 1 je pořadí anonymní třídy.
135
Implementace rozhraní pomocí metody využívající anonymní vnitřní třídu
public interface Info { void kdoJsem(); } class Usecka { int delka; Usecka(int delka) { this.delka = delka; } public Info informace() { return new Info() { // Vznikla třída odvozená od Object implementující Info. public void kdoJsem() { System.out.println("Usecka" + delka); }; // konec příkazu return – středník nutný } // konec metody informace() public class Test { public static void main(String[] args) { Usecka u = new Usecka(5); u.kdoJsem(); // chyba Info i = u.informace(); i.kdoJsem(); // Metoda je přístupná pouze přes referenční proměnnou rozhraní.
136
Proměnná typu rozhraní využívající anonymní vnitřní třídu
public interface Info { void kdoJsem(); } class Usecka { int delka; Usecka(int delka) { this.delka = delka; } public Info i = new Info() { public void kdoJsem() { System.out.println("Usecka" + delka); }; // konec deklarace proměnné i public class Test { public static void main(String[] args) { Usecka u = new Usecka(5); u.i.kdoJsem(); if (u instanceof Info) // Operátor instanceof funguje i na vnitřní anonymní třídy. System.out.println("u implementuje Info"); // Nevypíše se. if (u.i instanceof Info) System.out.println("u.i implementuje Info"); // Vypíše se. System.out.println(u.i.getClass().getName() + " implementuje Info"); // Vypíše Usecka$1 implementuje Info
137
Vnitřní třída vytvořená děděním
public interface Info { void kdoJsem(); } class Jmeno { public void kdoJeTo(Object o) { System.out.print(o.getClass().getName()); class Usecka { protected int delka; Usecka(int delka) { this.delka = delka; } class Obdelnik extends Usecka implements Info { private int sirka; Obdelnik(int delka, int sirka) { super(delka); this.sirka = sirka; public void kdoJsem() { new VnitrniJmeno().kdoJsem(); class VnitrniJmeno extends Jmeno { // vnitřní třída dědí z jiného objektu než vnější třída void kdoJsem() { // vnitřní třída nemusí překrývat všechny metody děděného objektu a to se využívá u GUI adaptérů. kdoJeTo(Obdelnik.this); // Takto ve vnitřní třídě získáme odkaz na instanci vnější třídy. // kdoJeTo(this); // this odkazuje na vnitřní třídu a vypsalo by Obdelnik$VnitrniJmeno. System.out.println(" " + delka + "x" + sirka); // Vnitřní třída má neomezený přístup k atributům vnější třídy. public class Test { public static void main(String[] args) { Info i = new Obdelnik(3, 6); i.kdoJsem(); // Vypíše Obdelnik 3x6.
138
Výjimky Výjimky jsou běhové chyby. Patří mezi bezpečnostní prvky Javy.
Vznikají za běhu programu. Vedle nich mohou být chyby syntaktické (kompilační) sémantické (Program pracuje jinak, než měl.) Patří mezi bezpečnostní prvky Javy. Java nutí programátora ve svém kódu reagovat na některé možné chybové stavy. Pokud programátor nenapíše ošetření výjimky, program se nepřeloží.
139
Druhy výjimek Třída Throwable (vyhoditelná) Balíky
mateřská třída pro všechny druhy výjimek Dědí z ní třída Error a její potomci. Představuje závažné chyby, které se mohou vyskytnout v JVM. Jednou z mála smysluplných reakcí na takovou chybu je zaslání programu, popisu prostředí a dalších okolností na Dědí z ní třída Exception a její potomci. Výjimky této třídy je programátor překladačem nucen obsloužit. Způsob ošetření je stejný pro chyby všech tříd. synchronní též kontrolované výjimky (checked exceptions) Synchronní proto, že vzniknou na programátorem definovaném místě. Mohou je vyvolat pouze určité metody. například vstupy/výstupy Dědí z ní třída RuntimeException a její potomci. Chyby zvané též asynchronní výjimky, vyvolává samotný JVM. Může je vyvolat jakákoliv metoda, proto překladač programátora nenutí reagovat. Programátor však na ně reagovat může, považuje-li to za užitečné. Například tak předejde výjimce při špatném uživatelském vstupu. Dědí z ní programátorem vytvořená třída synchronních výjimek. Balíky Třída Exception je z balíku java.lang. Její potomci mohou být z jiných balíků obsahujících související třídy.
140
Předání výjimky výše – deklarace výjimky
Programátor výjimku neumí nebo nechce ošetřovat a proto informaci o jejím výskytu předá volající metodě. tzv. propagace nebo šíření výjimek Metoda, ve které se výjimka vyskytla, se „zříká odpovědnosti“ za její zpracování. Toto předání odpovědnosti se jasně deklaruje již v hlavičce metody pomocí klíčového slova throws. Pokud by metoda dostala jméno souboru jako svůj parametr, pak nemá smysl výjimku typu „soubor nenalezen“ ošetřovat v této metodě. Je lepší sdělit volající metodě zprávu „nepovedlo se“ a nechat na main(), zda chce pokračovat dále. deklarace výjimky public static int prectiCislo() throws Exception {
141
Kompletní ošetření výjimky
Programátor výjimku zachytí a kompletně ošetří v metodě, kde se vyskytla. import java.util.Scanner; public class VyjimkaOsetrenaKompletne { static Scanner sc = new Scanner(System.in); public static int prectiCislo() { while (true) { // nekonečný cyklus try { // výkonný blok System.out.print("Zadejte číslo: "); int i = sc.nextInt(); break; // výskok z nekonečného cyklu, když není chyba } catch (Exception e) { // chybový kód – ošetření výjimky System.out.println("Chybně zadané číslo!"); sc.nextLine(); return i;
142
Ošetření výjimky a následné předání výše
Programátor výjimku částečně či kompletně ošetří v metodě, kde se vyskytla, a navíc pošle informaci o jejím výskytu do nadřazené úrovně. Vytváříme-li modul (reusable package), nevíme dopředu, jak si budoucí uživatel našeho modulu bude přát výjimku ošetřit. Ošetříme tedy výjimku jen tak, aby náš objekt zůstal provozuschopný (tj. „nespadl“) a výjimku propagujeme ven z metody.
143
Ošetření výjimky a následné předání výše – příklad
import java.util.*; public class VyjimkaOsetrenaAPropagovana { static Scanner sc = new Scanner(System.in); public static int prectiCislo() throws InputMismatchException { // deklarace předávané výjimky while (true) { // nekonečný cyklus try { // výkonný blok System.out.print("Zadejte číslo: "); int i = sc.nextInt(); break; // výskok z nekonečného cyklu, když není chyba } catch (InputMismatchException e) { // e je objekt třídy InputMismatchException. System.out.println("Chybně zadané číslo!"); sc.nextLine; e.printStackTrace(); // výpis chyby na konzoli, rozumná reakce na výjimku, když ji nechceme ošetřit složitěji throw e; // předání ošetřené výjimky výše return i; public static void main(String[] args) { try { int cislo = prectiCislo(); catch (Exception e) { System.out.println("Číslo nebylo zadáno správně hned napoprvé!"); Pomocí přetížené metody printStackTrace(PrintStream s) nebo System.setErr(), System.setOut() přesměrujeme chybový výpis do souboru, což je jediná možnost v případě aplikace s GUI.
144
Práce s výjimkami Naprosto nejhorší reakce na výjimku
Metoda nešíří výjimku do volající metody a blok catch neobsahuje kód. Chyba nastane ale nijak se neprojeví. Takto se to nikdy nesmí dělat!!! Seskupování výjimek Chceme různě reagovat na výjimky různých druhů vzniklé v jednom bloku try. Za blokem try může být neomezený počet bloků catch. Při výskytu výjimky se postupně odshora procházejí jednotlivé bloky catch. Jakmile vyhozená výjimka vyhovuje třídě výjimek uvedené v catch nebo její libovolné rodičovské třídě, provede se tento blok a ostatní bloky catch jsou přeskočeny. Nejdřív se musí uvést speciálnější druhy výjimek. Vyvolání výjimky Někdy je těžké výjimku vyvolat daty vstupujícími do programu, nebo chceme, aby výjimkou byla hodnota vyhovující datovému typu ale mimo námi povolený rozsah. try { if (n == 0) { throw new InputMismatchException(); } Nezneužívejte systém zachycení výjimek například tím, že místo break pro opuštění cyklu vyhodíte výjimku. Program bude pomalejší a výrazně nepřehlednější.
145
Vytvoření vlastní výjimky
pro vlastní třídy, ve kterých nedokážeme výjimku ošetřit class CisloMimoRozsah extends Exception { public CisloMimoRozsah (int limit) { super("Číslo nesmí být větší než " + limit + "."); } Objekt vlastní třídy výjimku třídy CisloMimoRozsah vyhodí. V části catch kódu volajícím objekt vlastní třídy můžeme vypsat řetězec vrácený metodou getMessage() objektu třídy CisloMimoRozsah, jméno třídy výjimky, tedy CisloMimoRozsah, metodou printStackTrace() objektu třídy CisloMimoRozsah. Potom stačí vytvořit třídu CisloMimoRozsah bez vnitřního kódu. class CisloMimoRozsah extends Exception {}
146
Využití vlastní výjimky
public static int zadejCislo(int limit) { int i; while (true) { try { System.out.print("Zadejte číslo: "); i = sc.nextInt(); sc.nextLine(); if (i > limit) { throw new CisloMimoRozsah(limit); } return i; } catch (CisloMimoRozsah e) { System.out.println(e.getMessage()); } catch (Exception e) { System.out.println("Chybně zadané číslo!");
147
Konstrukce try – (catch –) finally
Dvojici bloků try – catch je možné rozšířit o nepovinný blok finally. Blok finally se provede vždy, tedy i když je v blocích try nebo catch uveden příkaz return, vyvolána jiná i nezachycená výjimka, která je propagována výše. Typické použití uzavření otevřených souborů, destrukce objektů finally { if (fr != null) { fr.close(); } } V bloku try nemusí být vždy kód vyhazující výjimku ale například příkaz return 1; vykonaný za nějaké podmínky. Blok catch potom chybí. V bloku finally je kód, který musí být proveden za všech podmínek. Za blokem finally je příkaz return 0;, který se provede, když podmínka nenastala.
148
Adresáře a soubory Pro práci se soubory slouží třída File.
nezajišťuje čtení nebo zápis, metodami isDirectory() a isFile() rozlišuje adresáře od souborů. Poskytuje čtyři statické proměnné obsahující oddělovače v cestě k souborům pro daný operační systém.
149
Proměnné třídy File Oddělovač adresářů Oddělovač cest – znak „;“
UNIX (Linux, Solaris, …) Znak „/“ a není více disků. /adresar1/adresar2/soubor.txt MS Windows Znak „\“ a je více disků. c:\adresar1\adresar2\soubor.txt File.separatorChar, File.separatorString oddělovač adresářů jako char nebo String Oddělovač cest – znak „;“ c:\jdk1.6.0\bin;c:\jdk1.6.0\lib; System.getProperty("user.dir") Vrátí úplnou cestu pro aktuální adresář jako instanci třídy String. Možné parametry metody getProperty() určuje metoda getProperties().
150
Vytvoření instance třídy File
Třída File má konstruktory vyžadující jméno souboru nebo adresáře. To, že se vytvoří instance daného jména, ještě neznamená, že uvedený soubor nebo adresář fyzicky existuje. String aktDir = System.getProperty("user.dir"); // C:\java File soubAbs = new File(aktDir, "a.txt"); System.out.println(soubAbs.getAbsolutePath()); // C:\java\a.txt System.out.println(soubAbs.getName()); // a.txt System.out.println(soubAbs.getParent()); // C:\java File soubRel = new File("TMP" + File.separator + "a.txt"); System.out.println(soubRel.getAbsolutePath()); // C:\java\TMP\a.txt System.out.println(soubRel.getName()); // a.txt System.out.println(soubRel.getParent()); // TMP File soub = new File("a.txt"); System.out.println(soub.getAbsolutePath()); // C:\java\a.txt System.out.println(soub.getName()); // a.txt System.out.println(soub.getParent()); // null null protože nebyla uvedena ani relativní cesta
151
Vytvoření souboru nebo adresáře
Testování existence if (soub.exists()) System.out.println("Soubor existuje."); Vytvoření souboru else soub.createNewFile(); Metoda createNewFile() může vyvolat výjimku IOException z balíku java.io. Vytvoření adresáře Metodou mkdir() vytvoříme jeden adresář. Metodou mkdirs() vytvoříme více vnořených adresářů najednou. Rozlišení souboru od adresáře if (soub.isFile()) System.out.println("Je to soubor.");
152
Práce se souborem nebo adresářem
File soub = new File("b.txt"); File adr = new File("TMP"); System.out.println(new Date(soub.lastModified())); System.out.println(adr.length()); File jiny = new File("c.txt"); soub.renameTo(jiny); // přejmenuje b.txt na c.txt na disku adr.renameTo(new File("TMP-OLD")); // přejmenuje TMP na TMP-OLD na disku soub.delete(); // nevymaže c.txt, protože objekt soub je stále b.txt. adr.delete(); // nevymaže TMP-OLD // adresář TMP-OLD již vymazat nelze, protože nemáme příslušnou referenční proměnnou jiny.delete(); // skutečné vymazání c.txt
153
Výpis adresáře Třída File dává k dispozici i dvě metody pro výpis všech souborů a podadresářů v daném adresáři. Metoda list() zjistí pouze jména a uloží je pole typu String. Metoda listFiles() vrátí pole objektů typu File. Selektivní výpis adresáře V Javě je víc možností než pomocí masky typu "*.txt". Naprogramuje se třída implementující rozhraní FilenameFilter. V jeho jediné metodě accept() se rozliší, zda je soubor pro náš výběr přijatelný, či nikoliv. Pro vlastní výběr se použijí přetížené metody list() nebo listFiles() se skutečným parametrem reference na objekt třídy FilenameFilter. Stejný koncept má třídění pole objektů statická vnitřní třída pro třídění
154
Selektivní výpis adresáře – vytvoření tříd s filtry
class FiltrPripony implements FilenameFilter { String maska; FiltrPripony(String maska) { this.maska = maska; } public boolean accept(File dir, String name) { if (name.lastIndexOf(maska) > 0) return true; else return false; class FiltrVelikosti implements FilenameFilter { int velikost; FiltrVelikosti(int velikost) { this.velikost = velikost; File f = new File(dir, name); if (f.length() > velikost)
155
Selektivní výpis adresáře – použití tříd s filtry
public class Soubor { public static void main(String[] args) { String jmenoAktDir = System.getProperty("user.dir"); File aktDir = new File(jmenoAktDir); FiltrPripony filtrPr = new FiltrPripony(".java"); String[] jmena = aktDir.list(filtrPr); for (int i = 0; i < jmena.length; i++) System.out.println(jmena[i]); FiltrVelikosti filtrVel = new FiltrVelikosti(1000); File[] soubory = aktDir.listFiles(filtrVel); for (int i = 0; i < soubory.length; i++) System.out.println(soubory[i].getName() + "\t" + soubory[i].length()); }
156
Čtení ze vstupů a zápis na výstupy
Protože Java je programovací jazyk podporující distribuovaný a vícevláknový výpočet, nelze říci, že vstupní a výstupní zařízení je pouze soubor. Může to být i na síti nebo v paměti přístupné jinému programu nebo vláknu. Pro jakýkoliv přenos informace je nutné otevřít proud (stream). To, jak technicky probíhá čtení nebo zápis, není záležitost programu, ale JVM v závislosti na příslušném operačním systému. Pro četní a zápis dává Java Core API v balíku java.io k dispozici více než 50 tříd.
157
Proudy znaků a proudy bajtů
abstraktní třídy Reader a Writer 16bitová jednotka znamenající Unicode znak obdoba textového proudu v jiných jazycích proudy bajtů abstraktní třídy InputStream a OutputStream obdoba binárního proudu v jiných jazycích Nejdůležitější metody tříd proudů read(), write(), close() Všechny metody mohou vyvolávat výjimku třídy IOException nebo její podtřídy. Od každé ze čtyř základních abstraktních tříd je odvozeno zhruba deset dalších podtříd. Tyto třídy jsou vždy dvou typů: třídy pro fyzický přesun dat na určité zařízení nebo z něj pomocné třídy pro určité zpracování dat
158
Třídy pro fyzický přesun dat
zařízení paměť Do paměti čteme a z paměti zapisujeme typicky tehdy, když budeme data ještě nějakým způsobem zpracovávat. zařízení soubor Třídy začínají na File. zařízení roura komunikace metod nebo vláken jednoho programu Třídy začínají na Piped.
159
Pomocné třídy pro určité zpracování dat
Jsou často označovány jako filtry nebo vlastnosti. Při čtení je zpracování provedeno po fyzickém přesunu dat. Při zápisu je zpracování provedeno před fyzickým přesunem dat. Využití vyrovnávacích pamětí – bufferování BufferedReader a BufferedWriter pro znaky BufferedInputStream a BufferedOutputStream pro bajty Vrácení načteného znaku zpět do vstupního proudu PushbackReader pro znaky, PushbackInputStream pro bajty Formátovaný výstup PrintWriter pro znaky, PrintStream pro bajty Binární čtení nebo zápis základních datových typů DataInputStream a DataOutputStream pouze pro bajtové proudy Binární čtení nebo zápis libovolných objektů ObjectInputStream a ObjectOutputStream pouze pro bajtové proudy Konverze mezi znaky a bajty InputStreamReader, OutputStreamWriter Filtrování FilterReader a FilterWriter pro znaky FilterInputStream a FilterOutputStream pro bajty Spojování vstupních proudů SequenceInputStream
160
Neformátované čtení ze souboru a zápis do souboru
import java.io.*; public class IoZnaky { public static void main(String[] args) throws IOException { File frJm = new File("a.txt"); File fwJm = new File("b.txt"); if (frJm.exists() == true) { // Test existence omezuje vznik výjimky. FileReader fr = new FileReader(frJm); FileWriter fw = new FileWriter(fwJm); int c; while ((c = fr.read()) != -1) // konec souboru = -1 fw.write(c); fr.close(); fw.close(); } Proudy bajtů mají místo FileReader FileInputStream a místo FileWriter FileOutputStream. Vše ostatní je stejné.
161
Neformátované čtení ze souboru a zápis do souboru jinak
import java.io.*; public class IoZnaky { public static void main(String[] args) throws IOException { File frJm = new File("a.txt"); // Z objektu File lze zjistit velikost souboru. FileReader fr = new FileReader(frJm); FileWriter fw = new FileWriter("b.txt"); // Nemusím vytvářet objekt File. long delka = frJm.length(); int c; for (long i = 0; i < delka; i++) { c = fr.read(); fw.write(c); } fr.close(); fw.close(); Soubory s Unicode textem jsou čteny po bajtech. Většina současných souborových systémů je totiž osmibitová. Je možné změnit nastavení kódovacího schématu System.getProperty("file.encoding").
162
Zápis nové řádky do textového souboru
public static void soubor1() throws IOException { final String NOVY_RADEK = System.getProperty("line.separator"); FileWriter fw = new FileWriter("radka1.txt"); fw.write("Text1" + NOVY_RADEK + "Text2" + NOVY_RADEK); fw.write("Text3\nText4%n"); // „Text3“, znak s kódem 10, „Text4%n“ fw.close(); } public static void soubor2() throws IOException { FileWriter fw = new FileWriter("radka.txt"); BufferedWriter bfw1 = new BufferedWriter(fw); bfw1.write('a'); // fw.close(); by uzavřelo soubor, aniž by se do něj zapsala data z bufferu. bfw1.close(); // správné uzavření souboru // Aby nedošlo k tomuto omylu, je možné otevřít soubor následovně: BufferedWriter bfw2 = new BufferedWriter(new FileWriter("radka2.txt")); bfw2.write("Text1"); // bfw2 je jediná referenční proměnná na soubor. bfw2.newLine(); bfw2.write("Text2"); bfw2.close();
163
Parametry příkazové řádky
Aplikace v Javě (nikoli aplet) může využít libovolné množství parametrů (argumentů), které byly zadány v příkazové řádce současně se jménem programu při jeho spuštění. Některé operační systémy nemají terminál, takže program nebude 100% přenositelný. public static void main(String[] args) args je pole řetězců fungující stejně jako v jazyce C pole argv. V jazyce C byl navíc ještě parametr argc, ve kterém byl uložen počet parametrů. Java toto nepotřebuje, protože každé pole si s sebou nese svoji délku args.length. V jazyce C byl v argv[0] uložen název programu. V jazyce Java je v argv[0] uložen první parametr. Program v jazyce Java se musí jmenovat jako jeho třída. Bílé znaky oddělují parametry, což lze potlačit tím, že slova oddělená mezerou dáme do uvozovek. Znak uvozovky není součástí parametru. Je-li parametrem číslo, předá se také jako řetězec. int i = Integer.parseInt(args[0]); // převod parametru na číslo
164
Systémové akce Systémové akce spočívají v komunikaci programu s jeho okolím, kterým je operační systém, případně počítač či síť. Komunikace probíhá vždy prostřednictvím JVM. Program v Javě běží pod konkrétním operačním systémem, jehož prostředí je popsáno v systémových atributech. Java dává možnost tyto atributy číst čímž napomáhá k vytváření přenositelných programů.
165
Systémové atributy a zdroje
systémově závislé atributy Jsou přístupné pomocí třídy Runtime. zdroje (procesory, paměť) dostupné JVM, připojování dynamických knihoven, spouštění procesů v konkrétních prostředích Používání metod třídy Runtime může učinit program systémově závislým! Zjištění velikosti dostupné paměti Runtime r = Runtime.getRuntime(); // Je to metoda instance. // Kdyby byla statická: System.out.println("Celá paměť: " + r.totalMemory() + " bajtů"); // Runtime.totalMemory() System.out.println("Volná paměť: " + r.freeMemory() + " bajtů"); //Runtime.freeMemory() java –Xms500M –Xmx1000M Pamet spuštění programu Pamet tak, aby JVM alokoval 500MB paměti na začátku a 1000MB maximálně systémově nezávislé Jsou přístupné pomocí třídy System. Třída System zpřístupňuje systémové zdroje v systémově nezávislé formě. standardní vstupní a výstupní proudy členské proměnné třídy System proudy in, out, err systémové vlastnosti System.getProperties().list(System.out); informace o čase long t = System.currentTimeMillis(); // počet milisekund od Pro testy výkonnosti programu se používá rozdíl času před a po. garbage collector a finalizer násilné ukončení programu System.exit(-1); Raději nepoužívat, nelze se spoléhat, že budou uzavřeny otevřené proudy a soubory.
166
Vlákna V současných operačních systémech může běžet více programů najednou. Velmi rychle se střídají, předávají si řízení, jsou pseudoparalelní. Na počítači s více procesory mohou být skutečně paralelní. Program se také může dělit na části, které poběží současně. coroutines, kooperující procesy, light-weight processes, vlákna (threads) Oblasti použití vláken Časově náročné akce by měly být doprovázeny informováním uživatele, co se právě děje a kdy to skončí, případně umožnit uživateli další práci. Čekání na vstupy od uživatele je vhodné využít k akcím programu. například kontrola pravopisu Opakující se výpočty Například simulace pohybu více jednotlivých lidí podle jednoho kódu programu Úlohy typu producent-konzument Producent připravuje data, která konzument průběžně zpracovává.
Podobné prezentace
© 2024 SlidePlayer.cz Inc.
All rights reserved.