Obsah 1. ÚVOD................................................................................................................................................................ 3 2. ZÁKLADY JAZYKA ...................................................................................................................................... 4 PROMĚNNÉ ........................................................................................................................................................... 4 PRIMITIVNÍ DATOVÉ TYPY .................................................................................................................................... 5 Deklarace proměnné promitivního typu .......................................................................................................... 5 Konstanty......................................................................................................................................................... 6 Přetypování ..................................................................................................................................................... 7 Přetečení.......................................................................................................................................................... 7 VÝRAZY A OPERÁTORY ........................................................................................................................................ 8 Aritmetické operátory a příkaz přiřazení......................................................................................................... 8 Relační operátory ............................................................................................................................................ 9 Logické operátory............................................................................................................................................ 9 Bitové operátory ............................................................................................................................................ 10 Podmíněný výraz ........................................................................................................................................... 10 ZÁKLADNÍ KONSTRUKCE PROGRAMU ................................................................................................................. 10 Posloupnost ................................................................................................................................................... 10 Větvení ........................................................................................................................................................... 11 Cykly.............................................................................................................................................................. 12 Příkazy break a continue ............................................................................................................................... 14 VOLÁNÍ METOD .................................................................................................................................................. 15 ŘETĚZCE (TŘÍDA STRING)................................................................................................................................... 17 KOMENTÁŘE ...................................................................................................................................................... 19 3. TŘÍDY ............................................................................................................................................................ 20 ZÁKLADNÍ POJMY OBJEKTOVÉHO PROGRAMOVÁNÍ ............................................................................................ 20 DEKLARACE TŘÍDY V JAVĚ ................................................................................................................................ 22 Modifikátory tříd ........................................................................................................................................... 22 PROMĚNNÉ TŘÍD A INSTANCÍ .............................................................................................................................. 23 Deklarace proměnných.................................................................................................................................. 23 Práce s proměnnými ...................................................................................................................................... 24 METODY............................................................................................................................................................. 24 Modifikátory metod ....................................................................................................................................... 24 Typ návratové hodnoty .................................................................................................................................. 25 Vstupní parametry metody............................................................................................................................. 25 Předávání hodnot parametrů......................................................................................................................... 26 Přetěžování metod ......................................................................................................................................... 27 Konstruktory .................................................................................................................................................. 27 VYTVOŘENÍ INSTANCE ....................................................................................................................................... 28 ZRUŠENÍ INSTANCE ............................................................................................................................................ 28 ROZHRANÍ .......................................................................................................................................................... 29 4. POLE .............................................................................................................................................................. 31 JEDNOROZMĚRNÉ POLE ...................................................................................................................................... 31 VÍCEROZMĚRNÁ POLE ........................................................................................................................................ 32 PARAMETRY VSTUPNÍ ŘÁDKY............................................................................................................................. 33 SOUHRNNÝ PŘÍKLAD .......................................................................................................................................... 33 5. VÝJIMKY ...................................................................................................................................................... 35 DRUHY VÝJIMEK ................................................................................................................................................ 35 VYVOLÁNÍ VÝJIMKY .......................................................................................................................................... 36 ZPŮSOB OŠETŘENÍ VÝJIMKY ............................................................................................................................... 36 6. VSTUPY A VÝSTUPY.................................................................................................................................. 38 VSTUPNÍ PROUDY ............................................................................................................................................... 38 Čtení ze souboru ............................................................................................................................................ 40 Čtení z konzole............................................................................................................................................... 41 VÝSTUPNÍ PROUDY ............................................................................................................................................. 42 Zápis do textového souboru........................................................................................................................... 43 TŘÍDA STRINGTOKENIZER .................................................................................................................................. 44 DALŠÍ TŘÍDY A METODY PRO PRÁCI S PROUDY ................................................................................................... 45 Čtení ze sítě.................................................................................................................................................... 45 Komprimace a dekomprimace souborů ......................................................................................................... 46 TŘÍDA FILE ......................................................................................................................................................... 47 7. DYNAMICKÉ DATOVÉ STRUKTURY .................................................................................................... 49
1
Obsah KONTEJNERY ...................................................................................................................................................... 49 VYTVOŘENÍ KONTEJNERU .................................................................................................................................. 52 PROCHÁZENÍ KONTEJNERU - ITERÁTOR .............................................................................................................. 54 SOUHRNNÝ PŘÍKLAD .......................................................................................................................................... 56 8. GRAFICKÉ UŽIVATELSKÉ ROZHRANÍ ............................................................................................... 58 PRVNÍ APLIKACE ................................................................................................................................................ 58 VKLÁDÁNÍ DALŠÍCH KOMPONENT ...................................................................................................................... 59 OBSLUHA UDÁLOSTÍ........................................................................................................................................... 61 ROZLOŽENÍ KOMPONENT V RÁMCI OKNA............................................................................................................ 64 Rozložení BorderLayout ................................................................................................................................ 64 Rozložení FlowLayout ................................................................................................................................... 64 Rozložení GridLayout.................................................................................................................................... 65 Další rozvržení............................................................................................................................................... 65 Kombinování správců rozvržení .................................................................................................................... 65 NASTAVENÍ VLASTNOSTÍ KOMPONENT ............................................................................................................... 66 Třída Font...................................................................................................................................................... 66 Třída Color.................................................................................................................................................... 67 TLAČÍTKA A TEXTOVÉ POLE ............................................................................................................................... 67 TEXTOVÁ OBLAST .............................................................................................................................................. 68 ZAŠKRTÁVACÍ POLÍČKA A PŘEPÍNAČE ................................................................................................................ 71 ROZBALOVACÍ SEZNAM ...................................................................................................................................... 73 MENU ................................................................................................................................................................. 75 OKNA PRO OTEVÍRÁNÍ A UKLÁDÁNÍ SOUBORŮ ................................................................................................... 79 INFORMAČNÍ OKNA............................................................................................................................................. 80 KARTIČKY .......................................................................................................................................................... 81 DALŠÍ KOMPONENTY GUI .................................................................................................................................. 84 9. APPLETY....................................................................................................................................................... 85 TŘÍDY APPLET A JAPPLET .................................................................................................................................. 85 TAG APPLET .................................................................................................................................................... 86 PRVNÍ JEDNODUCHÝ APPLET .............................................................................................................................. 86 APPLET S PARAMETREM ..................................................................................................................................... 87 10. PŘÍLOHY....................................................................................................................................................... 89 PŘÍLOHA 1 – JAVA SDK OD FIRMY SUN ............................................................................................................. 89 Vytvoření a spuštění programu,..................................................................................................................... 89 Programy obsažené v Java SDK od firmy SUN............................................................................................. 90 PŘÍLOHA 2 – POPIS ORIGINÁLNÍ DOKUMENTACE JAVY OD FIRMY SUN.............................................................. 93 PŘÍLOHA 3 - KONVENCE PRO PSANÍ PROGRAMŮ V JAVĚ ..................................................................................... 95 Jména souborů .............................................................................................................................................. 95 Struktura zdrojového souboru Javy............................................................................................................... 95 Zarovnávání, odsazování............................................................................................................................... 96 Komentáře ..................................................................................................................................................... 96 Deklarace ...................................................................................................................................................... 97 Příkazy........................................................................................................................................................... 97 Mezery ........................................................................................................................................................... 98 Konvence pro pojmenovávání ....................................................................................................................... 98 PŘÍLOHA 4 – JAR – VYTVÁŘENÍ A POUŽÍVÁNÍ ARCHIVŮ ..................................................................................... 99 PŘÍLOHA 5 - KÓDOVÁNÍ ČESKÝCH ZNAKŮ V JAVĚ ........................................................................................... 101 PŘÍLOHA 6 - SEZNAM TERMÍNŮ A ZKRATEK ..................................................................................................... 103 LITERATURA A ODKAZY ................................................................................................................................... 106 Literatura..................................................................................................................................................... 106 Informace o Javě na Internetu..................................................................................................................... 106 Vývojová prostředí a nástroje pro Javu....................................................................................................... 107
2
Úvod
1. Úvod Java je objektově orientovaný programovací jazyk, navržený jako přenositelný mezi platformami jako je Windows, Linux, Solaris, atd. Často bývá považován za jazyk určený pouze pro Internet. V Javě lze samozřejmě programovat pro Internet, ale také v ní lze vytvářet aplikace, které pracují s databázemi nebo soubory a s WWW nemají nic společného. V Javě můžeme vytvářet tři základní druhy programů: -
aplety - programy běžící v rámci WWW stránky na stanici klienta v prohlížeči
-
servlety - programy běžící na WWW serveru
-
aplikace - programy, které se spouštějí na stanici
Aby byla zajištěna přenositelnost mezi platformami probíhá překlad a spuštění takto: -
překladač Javy nepřekládá zdrojový text do proveditelného kódu, ale do pseudokódu přenositelného na různé platformy
-
na počítači, kde chceme program spustit, musí být nainstalována JVM (Java Virtual Machine) pro danou platformu. JVM pracuje jako interpret a provádí postupně akce zapsané v pseudokódu
-
jestliže se jedná o aplet, v HTML kódu WWW stránky je zapsáno, jaký soubor s pseudokódem je třeba spustit. Prohlížeče umějí tento kód interpretovat (záleží na použité verzi Javy, někdy je třeba doinstalovat příslušné rutiny)
Java je poměrně novým programovacím jazykem, poprvé byla představena firmou Sun v roce 1995. Od té doby již vzniklo několik verzí, označovaných firmou Sun 1.0, 1.1, 1.2 a 1.3. Nejpodstatnější rozdíly jsou mezi verzemi 1.0 a 1.1, verze 1.2 přinesla především rozšíření knihoven. Java 1.2 bývá také označována jako Java2 (pozor Java 1.3 je také Java2 ne 3). V těchto skriptech se budeme zabývat především verzí 1.2 a vyšší. Pro práci s jazykem Java byly vytvořeny různé vývojové nástroje od různých firem. V našem kurzu budeme používat JDK (Java Development Kit) firmy Sun, který je volně dostupný ke stažení na Internetu na adrese JAVA.SUN.COM. Přehled dalších vývojových nástrojů naleznete v příloze těchto skript. Tato skripta jsou určena studentům kurzu IT_112 Úvod do programování v jazyce Java. Cílem těchto skript je tedy seznámit studenty s jazykem a ukázat jim práci s několika vybranými standardními třídami (API, Application Programming Interface, obsahuje těchto tříd více než tisíc), aby byli schopni vytvořit v Javě jednoduché aplikace.
3
Základy jazyka
2. Základy jazyka Napíšeme si první jednoduchý program v Javě, na kterém si vysvětlíme základy jazyka. Bude to program, který sečte dvě celá čísla a výsledek zobrazí na konzoli. public class PrvniProgram { public static void main(String[] args) { int scitanec1 = 5; int scitanec2 = 2; int soucet = scitanec1 + scitanec2; System.out.println("Muj prvni program "); System.out.println("Soucet je: " + soucet); } } Programovací jazyk Java rozlišuje malá a velká písmena. Hned od počátku je třeba si uvědomit, že je rozdíl, jestli napíšeme soucet nebo Soucet, v Javě se jedná o dva různé identifikátory. Java jako objektový jazyk používá téměř vždy objekty. Každý program je třídou a proto je v našem prográmku uvedeno public class PrvniProgram. Class je klíčové slovo Javy pro deklaraci třídy, PrvniProgram je jméno této třídy (identifikátor) a public modifikátor třídy, který označuje třídu jako veřejně dostupnou. Dále musíme dodržet pravidlo, že soubor, ve kterém bude zdrojový text uložen, se musí jmenovat stejně jako třída a mít koncovku .java. V našem případě se soubor jmenuje PrvniProgram.java. Pozor, je třeba rozlišovat velká a malá písmena i u jmen souborů, i když např. ve Windows se to zdá být zbytečné. Překladač kontroluje shodu názvu třídy s názvem souboru včetně malých a velkých písmen. Pokud má být program spustitelný, musí v něm definovaná třída obsahovat metodu main deklarovanou se všemi modifikátory uvedenými v programu (tj. public static). Jejich význam bude vysvětlen v následujících kapitolách. Dále náš program obsahuje blok příkazů, ve kterém je deklarace a použití proměnných (scitanec1, scitanec2, soucet) a spuštění metod.
Proměnné Při své činnosti si program ukládá data do proměnných či skupin proměnných. Popis proměnné se skládají ze tří částí: jména proměnné (identifikátoru), datového typu (např. celé číslo) a vlastního obsahu (hodnot, v případě tříd a rozhraní jsou součástí obsahu i metody). Jméno proměnné (identifikátor) musí začínat písmenem, po něm mohou následovat další písmena nebo číslice, délka není omezena. Ve jméně proměnné může být i podtržítko “_” a znak “$”1.
1
4
Znak $ není vhodné používat, neboť ho překladač používá při vytváření interních proměnných.
Základy jazyka Java je přísně typový jazyk, proto je vždy nutno uvést datový typ proměnné. Datové typy v Javě lze rozdělit do těchto skupin: •
•
primitivní typy: •
čísla (byte, short, int, long, float, double)
•
znaky (char)
•
logické hodnoty (boolean)
referenční typy: •
třídy (class)
•
rozhraní (interface)
•
pole (array)
Referenčním typům jsou věnovány samostatné kapitoly.
Primitivní datové typy Java používá pro celočíselné proměnné čtyři datové typy: long, int, short a byte, pro reálná čísla dva typy: double a float, pro znaky typ char a pro logické proměnné typ boolean. Velikost, jakou zabírají v paměti, a rozsah, který jsou schopny obsáhnout, vidíme v následující tabulce. Název typu long
Velikost (v bytech) 8
int
4
short byte double
2 1 8
float char boolean
4 2 1 bit
Rozsah -9 223 372 036 854 775 808 až +9 223 372 036 854 775 807 -2 147 483 648 až +2 147 483 647 -32 768 až +32 767 -128 až +127 ±1.797 693 134 862 315 70 E+308 ±3.402 823 47 E+38 65 536 různých znaků true nebo false
Implicitní hodnota2 0 0 0 0 0.0 0.0 \u0000 false
tabulka 1 Primitivní datové typy
Deklarace proměnné promitivního typu Při zavedení nové proměnné je nutné deklarovat identifikátor a datový typ, volitelně lze zadat i počáteční hodnotu. Deklaraci primitivního datového typu lze obecně zapsat takto: datový_typ jméno_proměnné;3 datový_typ jméno_proměnné = počáteční_hodnota;
2
Pouze pro proměnné třídy a instancí, neplatí v případě proměnných metod. Součástí deklarace proměnných mohou být modifikátory (public, static, final atd.) jejichž význam bude popsán později. 3
5
Základy jazyka Pokud tedy v našem programu sčítáme celá čísla, musíme pro ně nejdříve deklarovat proměnné: int scitanec1; int scitanec2 = 10; Počáteční hodnota proměnné může být uvedena nejen hodnotou, ale i výrazem, např.: long delka_dne=24 * 60 * 60; Výraz, kterým se přiřazuje hodnota, se musí dát vyhodnotit v době překladu popř. v okamžiku spuštění programu. Pokud proměnné při deklaraci nepřiřadíme počáteční hodnotu, kompilátor ji sám přiřadí (viz tabulka č.1) – bohužel se implicitní hodnota nepřiřazuje proměnným deklarovaným v rámci metod. Pokud počáteční hodnota bude mimo rozsah proměnné (např. byte b = 200), některé překladače přiřadí nesmyslnou hodnotu, některé ohlásí chybu.
Konstanty Konstanty se používají nejen při deklaraci proměnných, ale i v rámci výrazů. Celá čísla se zapisují obdobným způsobem jako v běžném životě, např. 9876; zápis nesmí obsahovat mezery či jiné oddělovače (např. tisíců). Je-li prvním znakem nula, znamená to, že číslo zapisujeme v osmičkové soustavě, začíná-li zápis čísla prefixem 0x, jedná se o číslo v hexadecimální (šestnáctkové) soustavě. Následující tři proměnné obsahují stejnou počáteční hodnotu: int i = 10; int j = 012; int k = 0xa; Celočíselná konstanta je vždy typu int, pokud potřebujeme konstantu jiného celočíselného typu, je třeba uvést přetypování (viz dále). Reálná čísla se zapisují buď s desetinnou tečkou (např. 0.25, 0.00002) nebo se používá tzv. semilogaritmický zápis, kdy číslo 1.54 * 106 se zapíše ve tvaru 1.54e6. Reálné konstanty jsou vždy typu double. Každý znak je v jazyce Java uložen v kódování Unicode ve dvou bytech. Znaky se píší v jednoduchých uvozovkách, a to buď v defaultním kódu operačního systému (v českých Windows 9x/NT je to kódová stránka CP1250), kdy překladač sám zajistí převod na příslušný znak Unicode, nebo použijeme zápis \uXXXX, ve kterém místo XXXX uvedeme hexadecimální kód příslušného znaku. char c = ’č’; char d = ’\u12ab’; Logické hodnoty – proměnné typu boolean mohou nabývat pouze dvou hodnot, true nebo false. V Javě lze vytvářet i pojmenované konstanty primitivních datových typů – definují se stejně jako proměnné s počáteční hodnotou s tím, že se na začátku uvede klíčové slovo final: final long DELKA_DNE=24 * 60 * 60;
6
Základy jazyka Takto definovanou proměnnou nelze již dále změnit, hodnota se přiřazuje v okamžiku definice. Zda se hodnota přiřadí při překladu či až při běhu programu záleží na výrazu, který přiřazuje hodnotu a na schopnosti optimalizace překladačem. Následující konstanta (náhodné číslo) se přiřadí až při běhu: final int NAHODNE_CISLO = (int) (Math.random() * 20); Je dobrým zvykem ve jménech konstant používat velká písmena.
Přetypování U celočíselné konstanty zapsané v programu se předpokládá datový typ int, u reálných konstant datový typ double. Například ve výrazu long vysledek = 10000*10000 se provede násobení konstant typu int a výsledek typu int se převede do proměnné typu long. Pokud chceme, aby byla konstanta jiného datového typu, je třeba zapsat ji s literálem. Literál pro long je L, pro float f a pro double d (lze používat malá i velká písmena). Tedy zapíšeme-li konstantu 100000L4 bude uložena jako long, zápis 10000f znamená, že číslo je uloženo jako reálné. Pokud se hodnota proměnné kratšího typu ukládá do proměnné delšího typu, provede Java převod automaticky (neexistuje nebezpečí ztráty informace). Například: int cislo1 = 12; long cislo2; cislo2 = cislo1; Pokud bychom ale potřebovali opačné přiřazení, je třeba provést explicitní přetypování a oznámit tak překladači, že případná ztráta informace nevadí, např.: float desetinneCislo1; double desetinneCislo2 = 0.123456789; desetinneCislo1 = (float) desetinnCislo2; Při operacích s číselnými proměnnými typu byte a short se tyto proměnné automaticky převádějí na typ int, tj. výsledek operace s dvěma těmito proměnnými je typu int. Při operaci s proměnnými/konstantami různě dlouhých typů je výsledek delšího typu, tj. výsledkem operace proměnných typu int a long je proměnná typu long, výsledkem operace typu int a double je proměnná typu double atd. Proměnná kratšího typu se převede na delší typ před provedením operace. Pokud trváme na uložení do kratšího typu, je nutné přetypovat výsledek operace: long cislo1 = 10000; int cislo2 = 100; int vysledek = (int) (cislo1 – cislo2);
Přetečení Pokud nastane situace, že informace, kterou do proměnné ukládáme, má větší hodnotu než je maximální hodnota typu, pak mluvíme o tzv. přetečení. Java neohlásí žádnou chybu, ale usekne přesahující část a program tak vyprodukuje špatný výsledek. Např. výsledkem operace 100000 * 100000 je číslo 4
U literálu je výjimečně jedno zda je písmeno malé nebo velké. Pro long je lepší používat velké L, malé l si lze splést s číslicí 1. 7
Základy jazyka 1 410 065 408 typu int místo správného 10 000 000 000, neboť tento výsledek je mimo rozsah typu int. K získání správného výsledku je potřeba aspoň jeden z operátorů označit jako long, tj. operace by měla vypadat následovně: 100000L * 100000. Doporučujeme používat proměnné typu long místo typu int a typ double místo typu float.
Výrazy a operátory Aritmetické operátory a příkaz přiřazení Se základními matematickými operátory jsme se už seznámili. Java používá pro sčítání +, pro odčítání -, pro násobení *, pro dělení / a pro zbytek po celočíselném dělení %. U přetypování a přetečení jsme už hovořili o problémech, které mohou nastat při těchto operacích. S dalším problémem se setkáváme u dělení celých čísel. Pokud v programu použijeme tento výraz double vysledek = 9/2 bude v proměnné vysledek hodnota 4. Výsledkem dělení dvou celých čísel je opět celé číslo bez ohledu na typ proměnné, do které výsledek ukládáme. Proto je i zde třeba použít přetypování nebo literál u jednoho z operandů, např.: double vysledek = 9d/2 V případě proměnných je potřeba jeden z operandů přetypovat, např. pokud cislo1 a cislo2 jsou typu int, double vysledek = ((double) cislo1) / cislo2 5. Java používá řadu zkrácených zápisů některých operací, jsou uvedeny v následující tabulce. Operátor
Příklad
+= -= /= *= %= ++
x += y x -= y x /= y x *= y x %= y x++ ++x x---x
--
Význam operátoru x=x+y x=x-y x=x/y x=x*y x=x%y x=x+1 x=x-1
tabulka 2 Význam některých aritmetických operátorů
Pro často se vyskytující operace - zvyšování nebo snižování hodnoty proměnné o jedničku – lze v Javě použít i zkrácený zápis x++, ++x, x--, --x. Tyto operátory lze (bohužel6) použít i na pravé straně 5
Výraz lze zapsat i ve tvaru double vysledek = (double) cislo1 / cislo2, ale zde je nejasné, zda se přetypování týká prvního operandu či výsledku (nutno znát, zda má větší prioritu přetypování či dělení). Zápis double vysledek = (double) (cislo1 / cislo2) je určitě chybně, neboť ten přetypovává až vlastní výsledek operace, která proběhne jako celočíselná. 6 U výrazů se obvykle předpokládá, že se při výpočtu nemění hodnoty proměnných použitých na pravé straně výrazu – operátory ++ a -- toto pravidlo porušují, což může vést k obtížně odhalitelným chybám. 8
Základy jazyka výrazu, kdy se rozlišuje, zda použijeme ++ jako předponu nebo příponu. Pokud se operátor ++ zapíše jako předpona, nejprve se zvýší hodnota proměnné a pak se použije, pokud je ++ zapsán jako přípona, pracuje se ve výrazu s původní hodnotou a teprve poté je zvýšena. Vysvětlíme si to v následujících příkladech. int puvodni = 10; int nova = puvodni++; Proměnná puvodni je nyní rovna 11 a proměnná nova je rovna 10. int puvodni = 10; int nova = ++puvodni; Proměnná puvodni je nyní rovna 11 a proměnná nova je rovna také 11. Pro operátor - - platí stejná pravidla.
Relační operátory Relační operátory se používají pro porovnání hodnot dvou číselných proměnných (pokud nejsou stejného typu, tak se převedou na delší typ), proměnných typu char a boolean. Výsledkem je vždy hodnota typu boolean, tj. buď pravda (true) nebo nepravda (false). Obvykle se používají v příkazu if a v příkazech cyklu jako podmínky7. Přehled relačních operátorů je v následující tabulce (pozor, nelze zaměnit pořadí znaků v operátorech):
relační operátor == != < > <= >=
význam rovná se nerovná se menší než větší než menší nebo rovno větší nebo rovno
tabulka 3 Relační operátory
Logické operátory Logické operátory slouží pro vyjádření vztahu mezi dvěmi proměnnými/výrazy typu boolean, tj. obvykle k vytváření složených podmínek. V Javě se používají operátory && (logická spojka AND) a || (logická spojka OR). Dále lze použít unární operátor !, který označuje negaci NOT následujícího výrazu. Chceme-li například otestovat, jestli je proměnná i větší nebo rovna 10 a současně menší než 50 zapíšeme podmínku takto: (i >= 10) && (i < 50) Výsledkem je hodnota true nebo false.
7
V textu se používá pojem podmínka místo správnějšího pojmu „výraz s výslednou hodnotou typu boolean“. Pojem podmínka je sice méně přesný, avšak často v kontextu lépe pochopitelný. 9
Základy jazyka Bitové operátory Java umožňuje pracovat i s jednotlivými bity celočíselných hodnot. Vzhledem k jejich výjimečnému použití zde nejsou popsány, v případě potřeby je nastudujte z on-line dokumentace.
Podmíněný výraz V Javě existuje ternární operátor ?:, který slouží k vytvoření tzv. podmíněného výrazu, syntaxe je: podmínka ? výraz1 : výraz2 Zapíšeme-li například tento podmíněný výraz: cislo1 < 5 ? cislo2++ : cislo2-Hodnota proměnné cislo2 se zvýší o jedničku (provede se výraz1) pokud je hodnota proměnné cislo1 menší než 5 (podmínka je splněna). Pokud je hodnota proměnné cislo1 větší nebo rovna 5 (tedy podmínka není splněna), hodnota proměnné cislo2 se sníží (provede se vyraz2). Pokud se na zápis podmíněného výrazu podíváme, pochopíme, že z důvodu přehlednosti je výhodnější používat příkaz if (viz dále).
Základní konstrukce programu Při popisu algoritmu používáme v podstatě tři základní struktury a to: •
posloupnost příkazů,
•
rozhodování na základě nějaké podmínky (větvení),
•
opakování činnosti na základě podmínky (cyklus).
S cykly souvisí příkazy pro změnu průběhu cyklu.
Posloupnost Posloupnost příkazů v Javě zapíšeme tak, že jednotlivé příkazy a deklarace oddělíme středníkem. int scitanec1 = 5; int scitanec2 = 2; int soucet = scitanec1 + scitanec2; Příkazy lze napsat i takto: int scitanec1 = 5; int scitanec2 = 2; int soucet = scitanec1 + scitanec2; ale vzhledem k nepřehlednosti doporučuji zapisovat na každý řádek jeden příkaz či deklaraci. Řadu po sobě jdoucích příkazů a deklarací můžeme (a v mnoha případech musíme) spojit do bloku pomocí složených závorek. { int scitanec1 =5; int scitanec2 = 2; int soucet = scitanec1 + scitanec2; } Po složené závorce už neuvádíme středník.
10
Základy jazyka Větvení Pro větvení má Java dva příkazy: if a switch. Příkaz if má podmínku a jednu nebo dvě větve. Syntaxe příkazu if s jednou větví je následující: if (podmínka) příkaz; Příkaz se provede pouze v případě, že podmínka platí tj. byla vyhodnocena jako true. Pokud podmínka byla vyhodnocena jako false, neprovede se nic a příkaz if končí. Syntaxe příkazu if se dvěma větvemi: if (podmínka) příkaz1; else příkaz2; Pokud je podmínka vyhodnocena jako true provede se příkaz1, pokud jako false provede se příkaz2. Místo jednoho příkazu je možno uvést blok příkazů uzavřený do složených závorek. Následující program vygeneruje dvě náhodná celá čísla v rozsahu 1 až 10 a poté zjistí jejich rozdíl (všimněme si též, jak se zapisují komentáře v Javě). public class IfElse { public static void main (String [] args) { /* * * * */
Generovní náhodného čísla. Metoda random() třídy Math vrací číslo typu double z intervalu 0 až 1. Pokud tedy chceme celé číslo od 1 do 10, musíme ho násobit 10 přičíst 1 a pomocí přetypování na int "odseknout" desetinnou část. int cislo1 = (int) (Math.random() * 10 + 1); System.out.println ("Cislo1=" + cislo1); int cislo2 = (int) (Math.random() * 10 + 1); System.out.println ("Cislo2=" + cislo2); if (cislo1 > cislo2) { int rozdil = cislo1 - cislo2; System.out.println ("Cislo1 je vetsi o:" + rozdil); } else { int rozdil = cislo2 - cislo1; System.out.println("Cislo2 je vetsi o:" + rozdil); }; //konec záporné větve i celého if }
} Promyslete si, co se stane v případě, že se vygenerují dvě stejná čísla.
11
Základy jazyka Málo používaný příkaz switch na základě hodnoty výrazu provádí příslušnou větev příkazu (větví obvykle bývá několik). Výraz musí být typu char, byte, short nebo int. Syntaxe je následující: switch (výraz) { case konstanta1: příkaz1; break; case konstanta2: příkaz2; break; case konstanta3: příkaz3; break; case konstanta4: příkaz4; break; // ... default: příkaz; }; V bloku může být několik příkazů začínajících klíčovým slovem case následovaným konstantou (tj. nelze zde uvést např. i > 5) a dvojtečkou. Jeden příkaz může začínat klíčovým slovem default následovaným dvojtečkou. Při provádění příkazu switch se nejprve vyhodnotí výraz a program přejde na větev case s odpovídající hodnotou nebo tuto hodnotu nenajde a přejde na větev default. Obvykle se každá větev ukončuje příkazem break, pokud jím však není větev ukončena, pokračuje se v provádění všech následujících větví, i když je u nich uvedena jiná hodnota než jakou má výraz (záleží na pořadí uvedení větví!). Postup provádění následujících dvou příkladů je tedy různý. Příklad1 switch ( i ) { case 1 :
Příklad2 switch ( i ) { case 1 : prikaz1; break;
prikaz1; case 2 :
case 2 :
prikaz2; default : prikaz3;
prikaz2; break; default : prikaz3;
}
} tabulka 4 Porovnání příkazu switch s break a bez něj
Hodnota proměnné i
Provedené příkazy příklad1
Provedené příkazy příklad2
1
prikaz1
2
prikaz2
jiná hodnota
prikaz3
prikaz1 prikaz2 prikaz3 prikaz2 prikaz3 prikaz3
tabulka 5 Výsledky porovnání dvou variant příkazu switch
Cykly V Javě jsou definovány tři druhy cyklů. První z nich je cyklus while s následující syntaxí: while ( podmínka ) příkaz;
12
Základy jazyka Provádění příkazu začíná vždy testem podmínky. Pokud je podmínka splněna (má hodnotu true), provede se příkaz a znovu se přejde na test podmínky. Při nesplnění podmínky provádění cyklu končí. Pokud podmínka není na začátku splněna, neprovede se příkaz v cyklu while ani jednou. int i=1; while (i <= 10) { // příkazy i++; } Nekonečný cyklus lze zapsat následovně: while (true) { // příkazy } Druhým a nejméně používaným typem cyklu je cyklus do-while s následující syntaxí: do příkaz while ( podmínka ); Tento cyklus začíná provedením příkazu a až poté se testuje podmínka. Dle výsledku se provádí znovu příkaz (hodnota true podmínky) nebo se končí (hodnota false). V cyklu do while se příkaz provede vždy alespoň jednou. Posledním typem cyklu je cyklus for, který používáme tam, kde známe počet iterací. Syntaxe: for ( inicializace; podmínka; krok) příkaz; Inicializací určujeme tzv. řídící proměnnou cyklu a její vstupní hodnotu. Pokud je splněna podmínka, provede se příkaz a poté se provede operace s řídící proměnnou cyklu uvedená v části krok. Poté se opět přejde na vyhodnocení podmínky. V následujícím příkladě se provede cyklus 10x (příklad odpovídá příkladu u cyklu while): for (int i = 1; i <= 10; i++) { // příkazy } Následující cyklus se provede 50x s tím, že řídící proměnná cyklu bude nabývat pouze sudých hodnot: for (int i = 2; i <= 100; i += 2) { // příkazy } Příkaz for lze přepsat do příkazu while následujícím způsobem: inicializace; while ( podmínka ) { příkaz; krok; }
13
Základy jazyka Příkazy break a continue Jazyk Java zná příkazy break a continue, které ovlivňují průběh zpracování příkazů v rámci cyklu (příkaz break se používá i v příkazu switch). Jestliže v těle cyklu použijeme break, výsledkem bude skok za konec tohoto cyklu. for (int i = 0; i <= 5; i++) { if (i == 3) break; System.out.println(i); } System.out.println("Konec programu"); Tato část programu bude mít tento výstup: 0 1 2 Konec programu Příkaz continue způsobí, že je přeskočen zbytek těla cyklu a znovu se testuje podmínka cyklu (v případě cyklu for se provede ještě krok řídící proměnné cyklu). Pokud ve výše uvedeném příkladu použijeme místo break příkaz continue, bude výstup vypadat takto: 0 1 2 4 5 Konec programu Break i continue lze uvést s návěstím a pak dojde ke skoku na toto návěstí, tj. lze opustit i několik do sebe vnořených cyklů. Návěstí ukončené dvojtečkou se uvádí v programu před začátkem vnějšího cyklu. /* CyklusSNavestim.java * ukazkovy priklad na pouziti navesti v cyklu while */ public class CyklusSNavestim { public static void main (String args[ ]) { int i = 0; vnejsicyklus: //definice návěstí "vnejsi cyklus" while (true) { //nekonečný cyklus while i++; int j = 0; while (true) { // vnořený nekonečný while j++; System.out.println("i: " + i + " j: "+j); if (j > i) { System.out.println("continue na vnejsicyklus (j > i)"); continue vnejsicyklus; }; if (j > 2) { System.out.println("break z vnitrniho cyklu (j > 2)"); break;
14
Základy jazyka }; }; System.out.println ("konec vnitrniho cyklu"); if (i > 3) { System.out.println("break z vnejsiho cyklu (i >3)"); break; }; }; System.out.println("konec vnejsiho cyklu"); }; } má následující výstup: i: 1 j: 1 i: 1 j: 2 continue na vnejsicyklus (j > i) i: 2 j: 1 i: 2 j: 2 i: 2 j: 3 continue na vnejsicyklus (j > i) i: 3 j: 1 i: 3 j: 2 i: 3 j: 3 break z vnitrniho cyklu (j > 2) konec vnitrniho cyklu i: 4 j: 1 i: 4 j: 2 i: 4 j: 3 break z vnitrniho cyklu (j > 2) konec vnitrniho cyklu break z vnejsiho cyklu cyklu (i > 3) konec vnejsiho cyklu
Volání metod V textu jsme se již mnohokrát setkali s voláním metod. Jak zjistíme později, rozlišují se metody třídy (statické) a metody instance. Jestliže potřebujeme použít metodu třídy, voláme ji uvedením jména třídy, tečky, jména metody a v kulatých závorkách parametry (nebo prázdné závorky pokud je metoda bez parametrů). V dokumentaci poznáte metody třídy dle toho, že ve specifikaci mají uvedeno klíčové slovo static. Například třída java.lang.Math poskytuje celou řadu metod třídy pro provádění různých matematických operací, některé z nich spolu s příklady jsou uvedeny v tabulce č.6 (podrobnější popis a přehled dalších je v on-line dokumentaci):
15
Základy jazyka Metoda static int abs (int a); static long abs (long a); static float abs (float a); static double abs (double a); static long round (double a); static int round (float a); static double sqrt (double a); static double cos (double a); static double sin (double a); static double tan (double a); static double pow (double a, double b); static double min (double a, double b); static float min (float a, float b); static int min (int a, int b); static long min (long a, long b); static double max (double a, double b); static float max (float a, float b); static int max (int a, int b); static long max (long a, long b);
Popis vrátí absolutní hodnotu parametru
Přiklad cislo2 = Math.abs(cislo1)
vrací nejbližší celé číslo výpočet druhé odmocniny trigonometrické funkce
cislo2 = Math.sqrt(cislo1)
umocňování, ab vrací menší z obou hodnot vrací větší z obou hodnot
tabulka 6 Metody třídy Math
Jak je v tabulce vidět (např. na metodě min), je možné v jedné třídě definovat více metod stejného jména s různým počtem, pořadím nebo typem parametrů. Tomuto říkáme přetěžování (overloading) metod. O tom, která z nich bude v programu použita, rozhodne překladač podle použitých parametrů. Pokud je některá metoda definována jen pro typ double (jako např. sqrt) není nutno přetypovávat hodnoty kratších typů na double, toto přetypování proběhne automaticky (tj. pokud proměnná cislo je typu int mohu napsat přímo Math.sqrt (cislo)). Jinak je tomu u typů návratových hodnot. Když metoda vrací hodnotu typu double, mohu ji uložit buď do proměnné typu double nebo ji mohu přetypovat do kratších typů (např. float či long). Lze tedy napsat: double odmocnina = Math.sqrt(cislo); float odmocnina = (float) Math.sqrt(cislo); následující zápis je chybný: float odmocnina = Math.sqrt(cislo);
// SPATNE
Druhým typem metod jsou metody instance, jediný rozdíl při volání těchto metod je, že uvádíme jméno instance a ne jméno třídy. Pro nejjednodušší programy a pro ladění se velmi často používají metody System.out.print(String s) nebo System.out.println(String s), které jsou určené pro výpis textového řetězce na konzoli. V tomto volání je tečka použita dvakrát, protože třída System obsahuje proměnnou out, která odkazuje na instanci třídy PrintStream, a pro tuto instanci vyvoláváme metodu print případně println, která je metodou třídy PrintStream.
16
Základy jazyka Ve volání metod se můžeme setkat i s více tečkami. S výjimkou metod z tříd (či třídy) z balíku (package) java.lang, je nutné uvádět úplné jméno balíku ve volání metod či odkazu na třídu (např. java.util.Date) nebo použít klauzuli import (viz následující kapitola).
Řetězce (třída String) Pro práci se řetězci (tj. s posloupností znaků jako je v našem příkladě "Muj prvni program") se v jazyce Java používá třída String. Třída String slouží k ukládání konstantních řetězců, jejichž hodnota se během činnosti programu nezmění (jedná se o příklad tzv. read-only třídy) – toto však neznamená, že stejný identifikátor nemůže v průběhu programu ukazovat na různé hodnoty (různé instance). Instanci třídy String lze definovat třemi způsoby. •
explicitně následující definicí: String s = new String("abcd");
•
implicitně, kdy překladač automaticky doplní potřebný kód pro vytvoření instance typu String: String s = "abcd";
•
implicitně, kdy se nedefinuje ani identifikátor (odpovídá konstantě primitivních datových typů): "abcd";
Pro práci s řetězci jsou definovány operátory + a += pro spojení dvou řetězců (instancí třídy String) do nové instance třídy String (do nového řetězce)8. Při použití operátoru += (např. retezec1 += retezec2) se odkaz na novou instanci přiřadí k identifikátoru uvedenému na levé straně výrazu. Různé způsoby deklarace a použití řetězců si ukážeme v následujícím příkladě. public class Retezce { public static void main (String args[ ]) { int cislo = 10; String retezec1 = "Vysledek je"; String retezec2 = new String ("korun"); System.out.println(retezec1 + " " + cislo + " " + retezec2); } } Příklad vypíše následující řádek: Vysledek je 10 korun Pokud se k řetězci připojuje operátorem + jednoduchá proměnná (int, long, ...), překladač automaticky zajistí převod proměnné na typ String (viz předchozí příklad, parametr příkazu println). Pokud se k řetězci připojuje instance objektu, překladač automaticky doplní volání metody toString(), která převede objekt na řetězec. Stejný způsob vyjádření objektu se používá i v některých metodách, např. v často používaných metodách print a println. Následující řádek: System.out.println (new Date()); který vypíše aktuální datum a čas, překladač interpretuje, jako kdyby vypadal následovně:
17
Základy jazyka Date datum = new Date(); System.out.println(datum.toString()); Při práci s řetězci v programu je třeba si uvědomit, že řetězce nejsou jednoduché proměnné, ale instance třídy String. Když potřebujeme porovnat obsah dvou řetězců, nelze použít následující porovnání retezec1 == retezec2
// SPATNE
protože tato podmínka porovnává, jestli oba identifikátory odkazují na stejnou instanci (na stejné místo v paměti). Pro porovnání obsahu dvou řetězců musíme použít metodu equals třídy String. Syntaxe porovnání řetězců retezec1 a retezec2 je následující: retezec1.equals(retezec2)
// DOBRE
a výsledek je typu boolean. Následující příkaz retezec1 = retezec2 nevytvoří kopii řetězce, ale přiřadí identifikátoru retezec2 odkaz na instanci, na kterou odkazuje identifikátor retezec1. Vytvořit kopii (tj. novou instanci se stejným obsahem) lze následujícím příkazem String retezec2 = new String(retezec1)9 Pro převody primitivních datových typů na řetězce je ve třídě String definována metoda třídy valueOf(). Například String retezec1 = String.valueOf(3.7629) uloží do objektu retezec1 hodnotu 3.7629 jako řetězec10. Stejného výsledku lze dosáhnout i následujícím výrazem String retezec1 = "" + 3.7629 kdy se využije vlastnosti automatické konverze proměnných na řetězec. Tato druhá varianta je však náročnější na rychlost zpracování, neboť zde se vedle konverze musí vytvořit objekt typu String s prázdným řetězcem a oba řetězce se musí spojit. Metoda String.valueOf s parametrem typu objekt vrací na výstupu výsledek metody toString() příslušného objektu. Ke třídě String existuje alternativní třída StringBuffer, která na rozdíl od třídy String není read-only při změnách nevytváří nové instance, čímž lze dosáhnout větší efektivity programu. Má obdobné metody jako třída String.
8
Jedná se o jediné případy přetížení (overloading) operátorů v Javě. Pro vytváření kopií objektů se doporučuje používat metodu clone(), třída String tuto metodu však nemá implementovánu. 10 Při prohlížení tohoto výrazu mohou u některých čtenářů vzniknout pochybnosti, proč zde není operátor new pro vytvoření nového objektu. Metoda String.valueOf sama uvnitř vytvoří novou instanci třídy String a na výstupu vrací pouze odkaz na tuto instanci, který se přiřadí k příslušnému identifikátoru. 9
18
Základy jazyka
Komentáře Pokud tvoříme program, je nezbytné uvádět v něm komentáře pro objasnění činnosti programu. Tyto komentáře poslouží jednak jiným programátorům pro pochopení vašeho programu, jednak i nám samotným, pokud se k programu vrátíme po delší době. Navíc firma Sun kromě překladače poskytuje i nástroj na automatické generování dokumentace z komentářů ve zdrojovém textu programu (javadoc). Krátký komentář je možno uvést přímo za příkaz, který chceme okomentovat. Komentář uvedeme se dvěma lomítky na začátku. Při překladu bude text od tohoto označení do konce řádku ignorován. Komentář delší než řádek začneme takto /* a ukončíme */, text mezi značkami je při překladu ignorován. Pokud chceme, aby byl komentář součástí automaticky generované dokumentace použijeme na začátku značku /** a ukončíme ho */.
19
Třídy
3. Třídy Základní pojmy objektového programování Jak už víme, je Java objektovým programovacím jazykem. V úvodu této kapitoly si objasníme základní pojmy objektové teorie. Objekt představuje souhrn dat a činností, které od něj můžeme žádat. Každý objekt má určité rozhraní, prostřednictvím kterého komunikuje s jinými objekty. Pro začátek si můžeme představit, že když tvoříme program pomocí objektů, skládáme ho obdobně jako např. stavbu z Lega. Je pro nás důležité, aby bylo možno je spojit a celek byl funkční, ale jak jednotlivé objekty dojdou uvnitř k výsledkům nás nezajímá. Když vytváříme program v objektovém jazyce, nepopisujeme jednotlivé objekty, ale třídy objektů. Třída (class) je obecný popis vlastností a činností objektů. Například budeme chtít pracovat se studenty, vytvoříme třídu Student, která bude obecně popisovat všechny studenty, jejich vlastnosti (např. jméno, příjmení, datum narození, adresu, absolvované kurzy, známky atd.) a činnosti (vytvoření nového studenta, zapsání předmětu, změna adresy atd.). Pokud potom vytvoříme konkrétního studenta Jana Nováka vytvoříme instanci třídy. Těchto instancí může k jedné třídě existovat libovolné množství. Java nám poskytuje řadu tříd, uložených v standardních knihovnách, které nám ulehčí práci (např. třídy pro práci se soubory, textovými řetězci, třídy poskytující matematické funkce, třídy pro tvorbu grafického rozhraní atd.). Třída popisuje vlastnosti jednotlivých instancí prostřednictvím proměnných (ekvivalentní jsou označení datové členy nebo datové atributy). Naše třída Student bude mít např. proměnnou jméno, tato proměnná bude v každé instanci nabývat různé hodnoty (např. Jan, Pavel, Petra, Tereza, Pavel,...). Při definici proměnné musíme určit její datový typ, tj. jestli v proměnné bude uloženo číslo, řetězec textu, instance jiné třídy atd. Takováto proměnná se označuje jako proměnná instance. Můžeme si také nadefinovat proměnnou počet, která bude součástí třídy Student a bude sloužit pro evidenci počtu studentů. Takováto proměnná není součástí jednotlivých instancí, existuje pouze jednou a je přístupná všem instancím třídy, označujeme ji jako proměnnou třídy. Dále třída obsahuje metody popisující chování (činnosti) jednotlivých instancí (metody instance) nebo společné všem instancím (metody třídy). Zapouzdření je jedna ze základních vlastností objektů. Jde vlastně o skrytí detailů implementace při zachování funkčnosti dostupné jiným objektům. Část objektu, která je dostupná ostatním objektům, se nazývá rozhraní objektu. Dědičnost je dalším ze základních rysů objektového programování. Umožňuje nám lépe využít již vytvořené třídy. Pokud již existuje třída, která částečně splňuje naše požadavky, pouze potřebujeme přidat nějakou vlastnost nebo činnost, využijeme v rámci objektového programování právě dědičnost. Již existující třídy lze rozšiřovat a doplňovat tak, že novou třídu definujeme jako potomka (odvozenou
20
Třídy třídu, podtřídu, dceřinou třídu) stávající třídy (nadtřídy, rodičovské třídy, bázové třídy). Potomek zdědí všechny proměnné a metody nadřízené třídy. Např. máme třídu Tvar, která obecně popisuje společné vlastnosti a činnosti všech dvourozměrných tvarů. Můžeme vytvořit několik dalších tříd, které budou tyto vlastnosti přebírat a rozšiřovat např. Kruh, Čtverec a Trojúhelník. Grafické znázornění pomocí zjednodušeného zápisu z UML vidíme na obrázku č. 1.
Tvar
Kruh
Čtverec
Trojúhelník
obrázek 1 Jednoduché znázornění dědičnosti pomocí UML
Do nové třídy můžeme přidat další proměnné a metody. Třída Tvar bude obsahovat činnost nakresli. Všechny třídy, jejichž předkem je třída Tvar, budou tedy obsahovat metodu nakresli. V našem případě se ale obecná metoda nakresli pro jednotlivé podtřídy nehodí, potřebujeme již kreslit konkrétní tvary. Tato situace však neznamená, že dědičnost nelze použít. Využijeme další z objektových vlastností a to je polymorfismus tj. vytvoříme novou metodu stejného názvu ale jiného obsahu a tou původní metodu nakresli překryjeme (v angličtině se používá termín overriding). V jedné třídě může být definováno několik metod stejného jména, které se liší pořadím a počtem parametrů. Tomuto říkáme přetěžování (overloading) metod. Například pro kreslení kruhu můžeme chtít vytvořit metodu, která nakreslí kruh barvou, jaká je právě nastavena v systému, a jindy budeme chtít metodu, která nakreslí kruh námi zadanou barvou. Nemusíme hledat jiný název pro kreslení se zadanou barvou, i tato metoda se může jmenovat nakresli a bude mít navíc parametr pro určení barvy. Obecně lze vytvořit i třídu, která má více přímých předků, tento jev se nazývá vícenásobná dědičnost. Java však umožňuje pouze jednonásobnou dědičnost tj. třída v Javě může mít jen jednoho předka ( to ale neznamená, že tento předek nesmí mít svého předka atd.). V jazyce Java jsou všechny třídy potomkem třídy Object, jedině tato třída nemá v Javě předka.
21
Třídy
Deklarace třídy v Javě Deklaraci třídy v Javě lze schématicky popsat následovně: [ package JménoBalíku; ] [ import JménoTřídy, …; ] public [final | abstract] class jmeno [extends JménoRodičovskéTřídy] [implements JménoRozhraní …] { proměnné třídy; proměnné instancí; vnořené třídy; konstruktory; metody; } případné neveřejné třídy; Klauzule package slouží pro zařazení třídy do určitého balíku (package). Pokud se tato klauzule neuvede, bude třída zařazena do tzv. nepojmenovaného balíku. Popis práce s balíky je nad rámec rozsahu těchto skript. Nepovinná klauzule import slouží pouze pro zjednodušení psaní odkazů na jednotlivé třídy. Např. pokud chci ve své třídě vytvořit instanci třídy java.util.Random, mám dvě možnosti, jak se na tuto třídu odkázat: •
použít celé jméno java.util.Random (přehlednější, jednoznačné, náročnější na psaní)
•
zadat na začátku klauzuli import java.util.Random a poté používat jméno Random
V klauzuli import lze zadat více tříd a lze též použít i hvězdičky. Např. zápis import java.util.*; umožňuje zkráceně psát jména všech tříd z balíku java.util. Používání hvězdiček však může vytvářet velmi nepříjemné a těžko odhalitelné chyby v případě, že existují duplicity ve jménech objektů v různých balících. Překladač Javy automaticky při překladu doplní import třídy java.lang, tj. nemusíme pro používání prvků tohoto balíku uvádět příslušný import.
Modifikátory tříd Před klíčovým slovem class lze použít klíčová slova public, final a abstract, jejichž význam a způsob použití si nyní vysvětlíme. Je-li třída definována jako public, může se na ni odkazovat jakákoli jiná třída. Pokud klíčové slovo public nepoužijeme, může být třída zpřístupňována pouze ostatními třídami z balíku. V jednom zdrojovém souboru může být nejvýše jedna třída, která je definovaná jako public.
22
Třídy Klíčové slovo final označuje třídu, od níž již nelze definovat potomky. Klíčovým slovem abstract označujeme třídu, od které nelze vytvářet žádné instance a která se používá jako předek pro další třídy (lze zde nadefinovat, co vše mají tyto třídy společné). Jedině abstraktní třída může obsahovat abstraktní metody. Je možná pouze kombinace public abstract. Definice a používání abstraktních tříd není součástí těchto skript. Jméno třídy by mělo začínat velkým písmenem. Pokud chceme novou třídu vytvořit jako potomka některé jiné třídy, uvedeme za jménem třídy klíčové slovo extends následované jménem třídy předka (třída musí mít pouze jednoho předka, pokud není uveden, automaticky se doplní jako předek třída Object11). Pokud chceme aby naše třída implementovala rozhraní, použijeme klíčové slovo implements následované jménem jednoho či více rozhraní. Je možno uvést obojí tj. dědění i implementaci rozhraní. V definici třídy není ani jedna část povinná, viz náš úvodní příklad, který obsahoval pouze metodu main. Jestli bude námi vytvářená třída obsahovat proměnné, vnořené třídy nebo konstruktory a kolik v ní bude metod, závisí na logice toho, co tvoříme.
Proměnné tříd a instancí Deklarace proměnných Proměnné mohou být buď primitivního typu nebo to jsou instance tříd. Vše co jsme si řekli o deklaraci primitivních typů, které používáme jako pomocné proměnné v metodách, platí i pro proměnné objektů, pouze s tím rozdílem, že před identifikátorem typu mohou být uvedeny některé modifikátory (public, protected, private, static, final). Modifikátory public, protected a private určují rozsah dostupnosti proměnné a může být uveden maximálně jeden z nich. Jestliže označíme proměnnou modifikátorem private, bude přístupná pouze ve třídě obsahující její definici. Proměnná deklarovaná jako private není dostupná ani potomkům této třídy. Proměnná označená jako protected je dostupná pro všechny třídy se stejného balíku a ve všech třídách vytvořených jako potomci této třídy, a to i v případě, že třída potomka je z jiného balíku. Proměnná označená jako public je dostupná z jakékoli třídy. Pokud není žádný z těchto modifikátorů uveden, je u proměnné definován implicitní (přátelský) přístup - proměnná je přístupná v jakékoli třídě, která je součástí stejného balíku a nepřístupná mimo něj. Je tedy rozdíl mezi deklarací s modifikátorem protected a bez modifikátoru. Modifikátor static určuje, že se jedná o proměnnou třídy. To znamená, že v dané třídě existuje právě jeden výskyt této proměnné a všechny instance této třídy jej sdílejí. Změna hodnoty této proměnné tedy znamená změnu pro všechny instance. Pokud tento modifikátor není uveden, jedná se o proměnnou instance. Každá vytvořená instance této třídy má svou kopii proměnné instance a její změna v jedné instanci se nedotkne ostatních instancí.
23
Třídy Modifikátor final označuje konstantu. Takto označené proměnné může být přiřazena hodnota pouze jednou. Jakékoli další přiřazení nové hodnoty je překladačem označeno jako chyba. Příklady: protected int pocet = 10; private float objem; public static final int MAXIMALNI_POCET = 100; protected String jmeno;
Práce s proměnnými Pokud potřebujeme s proměnnou pracovat v rámci metod třídy ve které byla definována, odkazujeme se na ni pouze jejím jménem. Jestliže potřebujeme s proměnnou pracovat v jiné třídě, odkazujeme se na ni v závislosti na typu proměnné. Když je to proměnná třídy, tak jménem třídy, tečkou a jménem proměnné. Když je to proměnná instance, tak jménem instance, tečkou a jménem proměnné. Je však třeba si uvědomit, že pokud byla proměnná definována jako private, není přímý přístup k této proměnné mimo třídu vůbec možný.
Metody Další část deklarace třídy je tvořena deklaracemi metod. Konstruktor je speciální metoda, o které si povíme později. Metoda je část spustitelného kódu. Metodě mohou být při volání předány parametry a metoda může vrátit hodnotu. Deklarace metody vypadá následovně: [ modifikátory ] typ_návratové_hodnoty jméno_metody ( [parametry] ) [ throws jméno_třídy ] Klauzule throws bude vysvětlena později v kapitole o výjimkách.
Modifikátory metod Mohou být tyto: public, protected, private, static, abstract, final, native, synchronized. Modifikátory public, protected a private mají stejný význam jako u proměnných a také přístupnost metody bez uvedení jednoho z těchto modifikátorů je stejná jako u proměnných. Modifikátor static určuje, že metoda je metodou třídy tj. voláme ji jménem třídy a před jejím volání nemusí být vytvořena žádná instance této třídy. Pokud tento modifikátor není uveden, je metoda metodou instance. Metodu označenou jako static nelze u potomka třídy předefinovat. Metoda, která je definována s modifikátorem final, je metoda, kterou již nelze předefinovat. To tedy znamená, že žádný z potomků této třídy nesmí obsahovat metodu stejného jména se stejným počtem a pořadím parametrů. Modifikátor final však neznamená, že metoda nemůže být přetížena. Abstraktní metoda (s modifikátorem abstract) musí za deklarací končit středníkem, nesmí tudíž obsahovat žádnou implementaci, pouze deklaraci návratového typu a případně parametrů. Abstraktní
11
Úplné jméno je java.lang.Object
24
Třídy metody mohou být deklarovány pouze v abstraktních třídách. Implementace této metody musí být uvedena u potomka abstraktní třídy, ve které je metoda uvedena. Není přípustná kombinace modifikátoru abstract s modifikátorem private ani static. Modifikátor native označuje metodu zapsanou v jiném programovacím jazyce. Modifikátor synchronized se používá k synchronizaci metod v rámci více vláken jednoho procesu.
Typ návratové hodnoty V deklaraci metody musí být vždy uveden typ návratové hodnoty. Pokud metoda nevrací hodnotu, je typ označen jako void. Metoda, která má definován jiný typ návratové hodnoty než void, musí být ukončena příkazem return vracejícím hodnotu odpovídajícího typu.
Vstupní parametry metody Za jménem metody uvádíme v kulatých závorkách seznam formálních parametrů. Počet parametrů není omezen, ale budete-li někdy psát metodu s více než pěti parametry, zamyslete se jestli do ní nesdružujete zbytečně mnoho činností. Při definování parametrů uvádíme jejich typ a uvnitř metody s nimi pracujeme jako s proměnnými. Jako oddělovač jednotlivých parametrů používáme čárku. U každého parametru musí být jeho typ uveden samostatně, zápis static int max (int a, b) není možný, je třeba uvést static int max (int a, int b). Skutečné hodnoty parametrů dosadíme při volání metody. Pokud metoda nemá žádné vstupní parametry, je třeba při deklaraci i při volání takovéto metody uvést za jejím jménem prázdné závorky. Příklad 1: metoda, která vrací hodnotu většího ze vstupních parametrů: static int max (int a, int b) { if (a > b) return a else return (b); System.out.println.(”Konec metody”); //toto se neprovede } V tomto příkladě jsme deklarovali metodu max (jméno metody by mělo začínat malým písmenem), která je metodou třídy, vrací hodnotu typu int a má dva parametry typu int. Vrácení většího z čísel je realizováno pomocí příkazu return. Příkazem return provádění metody končí. Nemá tedy smysl uvádět poslední řádek v našem příkladě. Také si všimněte, že hodnota výstupu z metody u příkazu return byla přiřazena pokaždé jinak, obě varianty (se závorkami i bez) jsou správné. Pokud je metoda max deklarována ve třídě Pokus a spouštíme ji v rámci jiné metody stejné třídy, může vypadat její volání následovně: System.out.println("Vetsi je :"+max(cislo1,cislo2)); nebo int vetsi = max(cislo1,cislo2);
25
Třídy Pokud je tato metoda volána z jiné třídy, vypadá její volání takto: System.out.println("Vetsi je :"+Pokus.max(cislo1,cislo2)); nebo int vetsi = Pokus.max(cislo1,cislo2); Metoda max je metodou třídy (modifikátor static), proto ji lze volat buď jen jménem nebo jménem třídy a jménem metody bez nutnosti vytvoření instance. Příklad 2: metoda pro jednoduchý tisk. static void tisk(String s){ System.out.println(s); } Zde jsme definovali metodu tisk, jako metodu s návratovým typem void a jedním parametrem typu String. V této metodě nemusí být uveden příkaz return a metoda končí provedením všech příkazů v ní uvedených. Pokud uvedeme v takovéto metodě příkaz return, musí být prázdný tj. nesmí za ním být uveden žádný výraz. Volání této metody je v podstatě stejné jako volání v minulém příkladě, pouze s tím rozdílem, že se uvádí jako samostatný příkaz. Např.: tisk("AHOJ"); Pokus.tisk ("Nejaky text");
Předávání hodnot parametrů Při předávání parametrů metodě je třeba si uvědomit, že primitivní typy jsou předávány jinak než referenční. Primitivní typy jsou předávány hodnotou, tj. po dobu činnosti procedury se vytvoří kopie původní hodnoty a s tou metoda pracuje. Po skončení činnosti metody je tato kopie zrušena, ale její hodnota není převedena do původní proměnné. Důsledky si ukážeme na následujícím příkladě. static int zvetseniOCtyri (int i){ i += 4; return i; } část metody main: int a, b =1; b = zvetseniOCtyri(a); System.out.println("a = "+ a+"
b = "+b);
Výpis bude vypadat takto: a = 1
b = 5
Hodnota proměnné a je použita jako vstupní hodnota pro spuštění metody zvetseniOCtyri, ale dál už se s ní nepracuje. Při spuštění metody se vytvoří lokální proměnná (zde pojmenovaná písmenem i) a její změna se do proměnné a nepromítne. Parametry referenčních typů jsou předávány odkazem, tzn. že se pracuje i v metodě se stejnými daty. Důsledek si nejlépe uvědomíme na příkladě:
26
Třídy public void pridej(StringBuffer s){ s.append ("konec");//pripoji do retezce text "konec" } část metody main: retezec = new StringBuffer ("Zacatek "); pridej(retezec); System.out.println(retezec); Výpis bude vypadat takto12: Zacatek konec
Přetěžování metod O přetížení metod se mluví v případě, že metody mají stejné jméno a liší se pouze typem, počtem nebo pořadím parametrů. Pozor metodu nelze přetížit pouze změnou typu návratové hodnoty. Obvykle se přetěžují metody v rámci jedné třídy, lze však přetížit i metody zděděné od předka. V předchozím příkladě jsme definovali metodu pridej s jedním parametrem typu String, která na konec řetězce přidá text "konec". Jestliže chceme vytvořit metodu, která na konec řetězce přidá text, který jí předáme jako parametr, nemusíme vymýšlet jiné jméno metody, stačí přetížit metodu pridej. K původní metodě pridej přibude tato: public void pridej(StringBuffer s1, String s2){ s1.append (s2);//pripoji do retezce text uloženy v s2 } Která metoda bude použita, závisí na použití parametrů při volání. Část metody main: retezec = new StringBuffer ("Zacatek "); pridej(retezec); //jeden parametr, vyvola se puvodni pridej System.out.println(retezec); pridej (retezec, " dalsi text"); //dva parametry, vola se nova varianta pridej System.out.println(retezec); Výpis bude vypadat takto: Zacatek konec Zacatek konec dalsi text
Konstruktory Konstruktory jsou metody volané při vytváření instancí. Platí pro ně několik specifických pravidel. Konstruktor se vždy jmenuje stejně jako třída a je to jediná metoda, která nemá, a ani nesmí mít návratovou hodnotu. To znamená, že neuvádíme návratový typ (ani void) a v kódu konstruktoru se nesmí objevit return s výrazem. Z významu konstruktoru jako metody pro vytvoření instance vyplývá, že je nesmysl použít modifikátor static. Modifikátory pro určení přístupnosti metody se používají. 12
V ukázce je použita třída StringBuffer a ne String, neboť třída String je read-only a nelze změnit hodnotu instance této třídy. 27
Třídy Konstruktor lze také přetížit, tj. vytvořit jich několik s různými parametry. Pokud není ve třídě žádný konstruktor vytvořen, překladač vytvoří implicitní konstruktor bez parametrů. Konstruktor nelze použít jinak, než při vytvoření instance (tj. pouze za příkazem new).
Vytvoření instance Při vytváření instance je nutné definovat typ (tj. od které třídy vytvářím instanci) a jméno instance (pro jména instancí platí stejná pravidla jako pro jména proměnných). Například jsme si nadefinovali třídu Kruh a chceme vytvořit instanci. Nejdříve nadefinujeme proměnnou, která bude na instanci odkazovat: Kruh kr; Pak je nutné pomocí new vytvořit v paměti vlastní instanci a odkaz na ni (referenci) přiřadit do proměnné: kr = new Kruh(); Při vytváření instance se automaticky spouští konstruktor – za slovo new se píše konstruktor a ne jméno třídy (konstruktor se ale vždy jmenuje stejně jako třída). Při vytváření instance samozřejmě nemusíme použít konstruktor bez parametrů, ale jakýkoliv z konstruktorů definovaných ve třídě. Deklaraci a vytvoření instance lze zapsat i jedním příkazem. Kruh kr = new Kruh(); Někdy Vám může připadat, že se vytváří instance objektu bez příkazu new. Např. výsledkem řádku String retezec1 = String.valueOf(3.7629) je nová instance retezec1 třídy String. V tomto případě proběhla operace new v rámci metody String.valueOf a odkaz na novou instanci se vrací jako návratová hodnota.
Zrušení instance Ukázali jsme si, jak vytvořit instanci, logicky by mělo být možné také instanci zrušit. V Javě se o rušení nepotřebných instancí se nemusíme starat – o uvolňování nepotřebných instancí se stará tzv. garbage collector (čistič paměti). Nepotřebná instance je instance, na kterou není odkaz z žádné referenční proměnné. Toho lze dosáhnout několika způsoby, nejčastější jsou následující:
28
Třídy •
skončí rozsah platnosti proměnné,
•
do proměnné vložíme odkaz na jiné místo v paměti, původní instance je tedy bez reference a tato část paměti bude uvolněna: Kruh kr = new Kruh( ); . . kr = new Kruh( );
•
do proměnné vložíme hodnotu null, která znamená, že proměnná neukazuje na žádnou instanci. Původní instance je tedy bez reference a paměť bude uvolněna. Kruh kr = new Kruh( ); . . kr = null;
V okamžiku, kdy zaniknou všechny odkazy na instanci, tato instance ještě nezaniká – zaniká až při nejbližším průchodu garbage collectoru, který se spouští na pozadí v pravidelných intervalech. Pokud je potřeba při zániku instance provést nějakou speciální akci (tj. nejenom uvolnit paměť), je možné nadefinovat speciální metodu finalize().
Rozhraní Java povoluje pouze jednoduchou dědičnost, tj. každá třída má právě jednoho přímého předka a všechny třídy mají společného předka – třídu Object. Jako kompenzaci nevýhod tohoto řešení Java zavádí použití rozhraní. Rozhraní definuje metody, které v něm nejsou implementovány, tj. deklarací rozhraní je jen hlavička rozhraní a hlavičky metod. Třída, která toto rozhraní implementuje, musí všechny metody implementovat. Může se tedy zdát, že rozhraní je totéž jako abstraktní třída, která má všechny metody abstraktní. Existuje však několik podstatných rozdílů: •
rozhraní nemůže deklarovat proměnné kromě konstant
•
třída může implementovat více rozhraní
•
třída implementující rozhraní zároveň dědí - buď od třídy Object nebo od třídy uvedené v deklaraci třídy jako předek
Rozhraní implementujeme u tříd, kterým chceme vnutit zcela konkrétní činnost nezávisle na dědičné hierarchii. Stejné metody (metody se stejnou hlavičkou a činností) mohou mít tedy třídy, které jinak nemají společné předky (kromě třídy Object). V balíku java.lang je nadefinováno několik rozhraní. Například rozhraní Comparable obsahující jedinou metodu int compareTo (Object o). Pokud nějaká třída implementuje toto rozhraní, říká o sobě, že dvě její instance lze porovnat a určit, která je větší nebo zda se rovnají. Toho se využívá např. v implementaci seznamů (viz dále) v metodě pro třídění (standardní metodou lze třídit pouze objekty,
29
Třídy které mají implementované toto rozhraní). Rozhraní Comparable mají implementovány například třídy String, Integer, Double, Character, File nebo Date. Jméno rozhraní se může objevit jako parametr v metodě (např. v metodě Collections.sort pro třídění seznamů je jako parametr uvedeno rozhraní Comparator). Při použití těchto metod se musí jako parametr uvést instance třídy, která příslušné rozhraní implementuje. Některé metody vracejí jako návratovou hodnotu instanci rozhraní. V tomto případě, může proměnná typu rozhraní využívat pouze metody definované v tomto rozhraní.
30
Pole
4. Pole Jednorozměrné pole Pole je datová struktura, která nám umožňuje pracovat s větším množstvím hodnot stejného typu. Pole si lze představit jako řadu hodnot. Pole má jen jeden identifikátor (jméno), pro práci s jednotlivými položkami používáme indexy. Následuje příklad pole se jmény dnů v týdnu. pondělí
úterý
středa
čtvrtek
pátek
sobota
neděle
← hodnota
0
1
2
3
4
5
6
← index
tabulka 7 Ukázka pole
Pole v Javě je zvláštní druh objektu a tudíž podobně jako u objektů se rozlišuje deklarace pole od vytvoření instance pole (alokace pole). Jednorozměrné pole lze deklarovat takto (dvě varianty pro totéž): typ [ ] jmenoPole typ jmenoPole [ ] kde typ určuje datový typ položek pole (např. int nebo String). Rozsah pole při deklaraci neuvádíme, deklarací pouze vytváříme proměnnou (odkaz) na pole. Několik příkladů deklarace pole následuje: int [ ] prvocisla String [ ] dnyvTydnu String[] args Poslední příklad je převzat z deklarace metody main – toto pole odkazuje na jednotlivé parametry z příkazové řádky při spuštění programu. Pole alokujeme pomocí příkazu new takto: jmenoPole = new typ [pocetPoložek]; prvocisla = new int [5]; dnyvTydnu = new String [7]; Obě části lze spojit dohromady, takže pokud chceme vytvořit pole prvních 5 prvočísel, zapíšeme to takto: int prvocisla [] = new int [5]; Na jednotlivé prvky pole se pak odkazujeme indexy 0 až n –1, kde n je počet prvků pole. Na první položku námi definovaného pole se tedy odkážeme prvocisla [0] a na poslední prvocisla [4]. Pokud se pokusíme použít index 5 nebo jiný mimo interval 0..4 bude ohlášena chyba. Java striktně kontroluje překročení mezí pole. Po vytvoření jsou v jednotlivých položkách pole jejich inicializační hodnoty, tj. např. pole celých čísel obsahuje ve všech položkách nuly. Lze vytvořit i pole s již naplněnými hodnotami, pro pole dnyvTydnu by pak deklarace a inicializace vypadala takto:
31
Pole String dnyvTydnu[] = {"pondělí", "úterý", "středa", "čtvrtek", "pátek", "sobota","neděle"}; V tomto případě se pole neinicializuje pomoci příkazu new. Práce s tímto polem je naprosto stejná jako s polem vytvořeným pomocí new – prvky inicializovaného pole nejsou konstanty (tj. lze je měnit). Každé pole má definovanou konstantu length, ve které je uložen počet prvků pole. Lze se na ni odkázat jménem pole, tečkou a jménem length. Tedy pro naše příklady: System.out.println("Pole dnyvTydnu ma " + dnyvTydnu.length + " prvku"); for (int i = 0; i < dnyvTydnu.length; i++) { System.out.print(dnyvTydnu[i] + " "); };
Vícerozměrná pole V Javě lze vytvářet i vícerozměrná pole, deklarace vypadají takto: int poleDvojrozmerne [] []; Inicializovat toto pole lze několika způsoby. Na následujícím řádku int poleDvojrozmerne [] [] = new int [2] [3]; vznikne pole se dvěma řádky a třemi sloupci, položky jsou naplněny nulami. Při inicializaci lze zadat počáteční hodnoty. Následuje příklad vytvoření pole se dvěma řádky a třemi sloupci, položky budou naplněny zadanými hodnotami: int poleDvojrozmerne [ ] [ ] = { { 1, 2, 3, }, { 4, 5, 6, }, } V Javě je možná i postupná inicializace jednotlivých rozměrů pole: int poleDvojrozmerne [ ] [ ] = new int [2] [ ]; Takto vznikne pole, u něhož ještě není určen počet sloupců. Toto umožňuje vytvářet poněkud nezvyklá pole, která budou mít v každém řádku jiný počet prvků13. poleDvojrozmerne [0] = new int [3]; poleDvojrozmerne [1] = new int [5]; Takto vzniklé pole má v prvním řádku tři a ve druhém pět prvků. Všechny prvky mají hodnotu nula. Pro zjištění počtu prvků lze opět použít proměnnou length. Počet řádků našeho dvojrozměrného pole zjistíme takto poleDvojrozmerne.length, počet prvku prvků prvního řádku poleDvojrozmerne [0].length a pro další řádky analogicky. Lze vytvářet i pole tří a více rozměrná. Při deklaraci a inicializaci platí stená pravidla jako u dvojrozměrných. V případě, že pole vytváříme po částech, nesmíme přeskakovat rozměry. 13
V Javě se vytvářejí pole polí, ne klasická dvourozměrná pole známá např. z Pascalu.
32
Pole Lze tedy napsat int poleTroj [] [] [] = new int [5] [5] [ ]; a poslední rozměr určit později, nelze však napsat int poleTroj [] [] [] = new int [5] [ ] [5]; Pole je referenční typ, takže pokud pole použijeme jako parametr metody, je předáno odkazem.
Parametry vstupní řádky Řekli jsme si, že pokud má třída poskytnout možnost spuštění programu, musí obsahovat metodu main definovanou jako public static void main (String [] args). Jako parametr metody je tedy použito jednorozměrné pole řetězců. Znamená to, že při spuštění programu můžeme za příkaz java a jméno třídy uvést libovolný počet parametrů oddělených mezerou, které budou uloženy do pole args. Pole args má samozřejmě také proměnnou length, která udává kolik parametrů uživatel zadal. Pokud nejsou zadány žádné parametry, má proměnná args.length hodnotu nula. V případě, že jeden parametr má být text s mezerami, je možné zadat ho v uvozovkách např. "Dobrý den". Následující příklad ukazuje, jak z příkazové řádky načíst desetinné číslo představující poloměr kruhu a na konzoli pak vypsat jeho obvod a obsah. Parametr je načten jako String, je tedy nutný převod na číslo aby byl možný výpočet. Zatím neumíme ošetřit případ, že uživatel na příkazové řádce zadá chybný parametr (tj. např. java ObsahKruhu ahoj).
public class ObsahKruhu { public static void main (String [] args) { if (args.length == 0) { //osetrime moznost ze uzivatel nezadal parametr System.out.println("Nebyl zadan parametr"); System.exit (0); //ukonci cely program }; double polomer = Double.valueOf(args[0]).doubleValue(); //prevedeme parametr ze Stringu na cislo typu double double obvod = 2*Math.PI*polomer; //Math.PI je konstanta tridy Math double obsah = Math.PI*polomer*polomer; System.out.println("Kruh s polomerem "+polomer+" ma obsah " +obsah+" a obvod "+ obvod); } }
Souhrnný příklad Použití polí je ukázáno v následujícím programu, ve kterém se nadeklaruje pole o deseti prvcích typu int a naplní se náhodnými čísly z intervalu 0 až 100. Poté se pole vypíše, setřídí a znovu vypíše.
33
Pole import java.util.*; public class Pole { public static void main(String[] args){ int poleCisel [] = new int [10]; Random nahodneCislo = new Random (); for (int i = 0; i<10; i++){ poleCisel[i] =Math.abs(nahodneCislo.nextInt() % 100); }; System.out.println("Nesetridene pole"); vypisPole(poleCisel); trideni(poleCisel); System.out.println("Setridene pole"); vypisPole(poleCisel); } static void vypisPole (int pole []){ for (int i = 0; i <pole.length; i++){ System.out.print(" " + pole [i]); }; System.out.println(); } static void trideni (int pole []){ boolean vymena = true; while (vymena){ vymena = false; for (int j = 0; j< (pole.length -1); j++){ if (pole[j] > pole [j+1]){ int pomocna = pole[j]; pole[j]= pole[j+1]; pole[j+1] = pomocna; vymena = true; } } } } }//konec tridy Pole
34
Výjimky
5. Výjimky Při vytváření programu je třeba vždy zohlednit to, že za běhu programu se mohou objevit chyby, které je potřeba ošetřit. Například uživatel se snaží otevřít neexistující soubor pro čtení nebo místo čísla vloží na vstupu text.
Druhy výjimek Když v programu dojde k chybě, vznikne výjimka – přeruší se zpracování programu, vytvoří se objekt s informacemi o chybě a pokračuje se kódem, který ošetří chybu. Předkem pro všechny výjimky je třída Throwable a jejími potomky pak třídy Error a Exception. Java také umožňuje tvorbu vlastních výjimek. Hierarchie tříd pro chyby a výjimky je zachycena na následujícím obrázku.
Object
Throwable
Error
Exception RuntimeException
obrázek 2 Hierarchie výjimek
Se třídou Throwable se většinou nepracuje, poskytuje totiž velmi obecnou informaci, že nastala nějaká chyba. Třída Error reprezentuje chyby systému Javy (např. nedostatek paměti – OutOfMemoryError, přetečení zásobníku - StackOverflowError, chyby v souboru class – ClassFormatError, ClassCircularityError, VerifyError, NoClassDefFoundError), v programu se obvykle neošetřují (některé ani nelze ošetřit). Program skončí chybovými výpisy. Třída Exception a její potomci, mimo větev RuntimeException, jsou označovány jako kontrolovatelné výjimky a v našich programech na ně musíme reagovat. Vyskytují se v souvislosti s voláním určitých metod. Mezi typické zástupce patří chyby vzniklé při práci se vstupy a výstupy.
35
Výjimky Třída RuntimeException a její potomci reprezentují chyby, na které lze také úspěšně reagovat, ale u kterých Java nevyžaduje jejich ošetření. Jsou to například ArithmeticException, ArrayIndexOutOfBoundsException, NullPointerException nebo NumberFormatException.
Vyvolání výjimky Při psaní svých metod můžeme vyvolat výjimku příkazem throw, což může vypadat následovně: if (t == null) throw new NullPointerException(); Vyvoláním výjimky se přeruší provádění metody. Při vyvolání výjimky můžeme předat jako parametr i podrobnější popis výjimky: if (t == null) throw new NullPointerException("t = null");
Způsob ošetření výjimky Java poskytuje mechanismus na zachycení a ošetření výjimky neboli pro předání výjimky výše. Pokud tvoříme metodu, ve které může dojít k výjimce, a my ji v rámci této metody nechceme nebo neumíme ošetřit, musíme informaci o ní předat do nadřazené úrovně, tj. metodě, která naši metodu vyvolala. Toho dosáhneme tím, že v hlavičce metody použijeme klíčové slovo throws14 a jméno třídy výjimky. Například : public static String ctiVetu ( ) throws IOException { ... } je metoda která předává výše výjimku, která může vzniknout při čtení ze souboru. Pokud chceme výjimku ošetřit v metodě sami, uzavřeme část kódu, ve kterém může vzniknout chyba, do bloku uvedeného slovem try. Po tomto bloku následuje alespoň jeden blok začínající klíčovým slovem catch v závorce následovaný typem (jménem třídy) výjimky a jménem proměnné, ve které budou uloženy informace o chybě (obvykle se používá jméno proměnné e). Těchto bloků catch může být více. Platí však, že jednotlivé bloky jsou procházeny postupně a první, který vyhovuje (tj. výjimka, která nastala, je instancí uvedené třídy nebo jejího potomka), tuto výjimku ošetří. Praktické použití si ukážeme na rozšíření příkladu pro výpočet obvodu a obsahu kruhu z minulé kapitoly. Pokud uživatel zadá chybný parametr (tj. nelze jej převést na číslo typu double), bude při spuštění metody Double.valueOf (args[0]) vyvolána výjimka NumberFormatException která je potomkem třídy RuntimeException. V předchozí kapitole jsme možnost nezadání parametru ošetřili pomocí příkazu if, nyní použijeme mechanismu zachycení výjimky. Pokud se pokusíme použít položku pole, která neexistuje, vyvoláme výjimku ArrayIndexOutOfBoundsException (když uživatel nezadá žádný parametr, neexistuje položka pole args s indexem 0).
14
Slovo throws se do češtiny často překládá jako odmítání a z tohoto důvodu se často používá termín odmítnutí výjimky – nám připadá vhodnější termín předání výjimky výš.
36
Výjimky public class ObsahKruhu2 { public static void main (String [] args) { double polomer = 0; try { polomer = Double.valueOf(args[0]).doubleValue(); } catch (NumberFormatException e) { System.out.println("Zadany parametr neni cislo"); System.exit(0); } catch (ArrayIndexOutOfBoundsException e ) { System.out.println("Nebyl zadan parametr"); System.exit(0); } double obvod = 2*Math.PI*polomer; double obsah = Math.PI*polomer*polomer; System.out.println("Kruh s polomerem "+polomer +" ma obsah "+obsah+" a obvod "+ obvod); } }
37
Vstupy a výstupy
6. Vstupy a výstupy Pro práci se vstupy a výstupy nám Java poskytuje celou řadu tříd a jejich metod. Jsou uloženy v balíku java.io. Tato knihovna je založena na mechanizmu tzv. vstupních a výstupních proudů (streamů). Java poskytuje v podstatě čtyři základní třídy pro tvorbu proudů. Odlišuje vstupní a výstupní proudy a čtení po bytech nebo znacích. Pro vstupní bytově orientovaný proud je základní abstraktní třídou InputStream a pro bytově orientovaný výstup OutputStream. Pro znakové vstupy pak třída Reader a znakové výstupy třída Writer. Od každé z těchto tříd existuje několik potomků pro čtení/zápis z různých typů vstupních zařízení (soubory, roury, ...). Každá z abstraktních tříd (InputStream, OutputStream, Reader a Writer) má specifického potomka – abstraktní třídu, která je předkem filtrů tj. tříd, které přidávají další funkce pro jednotlivé potomky příslušné třídy. Mimo třídy pro proudy jsou v balíku java.io také třídy File (pro práci se soubory a adresáři), třída StreamTokenizer (pro rozdělení vstupního proudu na části), třídy definující různé výjimky a některé další.
Vstupní proudy Pro vstupy tedy slouží proudy založené na třídě InputStream nebo Reader. Na obrázku vidíme třídu InputStream a její potomky. Struktura dědičnosti je u třídy Reader obdobná.
InputStream
PipedInputStream SequenceInputStream
StringBufferInputStream
FileInputStream
ByteArrayInputStream FilterInputStream
BufferedInputStream
DataInputStream LineNumberInputStream
PushbackInputStream
obrázek 3 Hierarchie dědičnosti třídy InputStream
Třídy pro vstup lze rozdělit do čtyř samostatných skupin: třídy pro vytvoření vstupního proudu, třídy poskytující další vlastnosti vstupnímu proudu, třídy pro vytvoření readeru a třídy pro rozšíření funkcí readeru. V následujících tabulkách je uveden jejich přehled. 38
Vstupy a výstupy Třída ImputStream FileInputStream PipedInputStream SequenceInputStream ByteArrayInputStream StringBufferInputStream
použití abstraktní třída, která definuje základní metody pro čtení po bytech čtení ze souboru, parametrem konstruktoru je String se jménem souboru nebo object typu File čtení z roury (z objektu, do kterého zapisuje PipedOutputStream) vytvoří jeden vstupní proud ze dvou vstupních proudů, které jsou parametrem konstruktoru čtení z pole bytů v paměti, které je parametrem konstruktoru převede String na InputStream, doporučuje se používat místo toho StringReader
tabulka 8 Třídy pro vstup po bytech
Třída FilterInputStream BufferedInputStream DataInputStream LineNumberInputStream PushbackInputStream
použití předek tříd, které čtou z jiného InputStreamu a přidávají k tomu další funkčnost vytváří buffer pro čtení, čímž je toto čtení efektivnější ve spolupráci s třídou DataOutputStream umožňuje přenášet údaje ve formátu přenositelném mezi různými platformami přidává metodu pro číslování čtených řádků, doporučuje se použít LineNumberReader umožňuje vrátit část přečtených bytů zpět do vstupního proudu
tabulka 9 Třídy přidávající funkčnost pro čtení po bytech
Pro čtení po znacích byla vytvořena třída Reader a její potomci. Měly by se používat vždy, kdy se čte text, neboť v této třídě je garantována správná obsluha znakových sad a převod textu do vnitřního kódování Javy (do znakové sady Unicode). V následující tabulce je přehled tříd pro vytvoření readeru a jejich srovnání s potomky třídy InputStream: Třída Reader InputStreamReader FileReader PipedReader CharArrayReader StringReader
použití abstraktní třída, která definuje základní metody pro čtení po znacích převádí InputStream na Reader čtení ze souboru, parametrem konstruktoru je String se jménem souboru nebo object typu File čtení z roury (z objektu, do kterého zapisuje PipedWriter) čtení z pole znaků v paměti, které je parametrem konstruktoru převede String na Reader
odpovídající InputStream InputStream FileInputStream PipedInputStream ByteArrayInputStream StringBufferInputStream
tabulka 10 Třídy pro čtení po znacích a jejich obdoba pro čtení po bytech
39
Vstupy a výstupy Třída FilterReader BufferedReader LineNumberReader PushbackReader
použití předek tříd, které čtou z jiného Readeru a přidávají k tomu další funkčnost vytváří buffer pro čtení, současně přidává metodu ReadLine() pro čtení po řádcích, přidává metodu pro číslování čtených řádků umožňuje vrátit část přečtených znaků zpět do vstupního streamu
odpovídající InputStream FilterInputStream BufferedInputStream LineNumberInputStream PushBackInputStream
tabulka 11 Třídy pro rozšíření funkčnosti readeru
Ke třídám SequenceInputStream a DataInputStream neexistují odpovídající readery.
Čtení ze souboru Nejčastěji používaným vstupem je textový soubor. Pokud chceme přečíst tento soubor po řádcích, je nutné si pro to vytvořit vhodný vstupní proud. Pro čtení po znacích se souboru A.TXT si můžeme připravit instanci třídy FileReader takto: FileReader ctiZnak = new FileReader ("a.txt"); a poté lze číst jednotlivé znaky z tohoto souboru pomocí metody read ( ) int znak = ctiZnak.read ( ); Většinou však chceme číst po řádcích a pro takovéto čtení musíme instanci třídy FileReader ještě "zabalit" do filtru BufferedReader, který nám to umožní: BufferedReader radek = new BufferedReader (ctiZnak); a pak použít metodu readLine ( ) String s = radek.readLine(); Obě tyto třídy poskytují také metodu close() pro zavření souboru. Pro testování konce souboru používáme při čtení po bytech hodnotu –1 nebo hodnotu null při čtení po znacích. Následující kód ukazuje jak přečíst a na konzoli vypsat textový soubor a.txt. import java.io.*; public class Cteni { public static void main (String [] args){ try { BufferedReader vstup = new BufferedReader (new FileReader ("a.txt")); String s; while ((s = vstup.readLine()) != null) System.out.println (s); vstup.close(); } catch (IOException e){ System.out.println ("Chyba na vstupu souboru a.txt"); } } }
40
Vstupy a výstupy Jak již bylo řečeno v předchozí kapitole, některé výjimky je nutno ošetřit. K těmto výjimkám patří IOException a proto je její odchycení (případně poslání výše) nutné. Bez ošetření výjimky IOException není tento program přeložitelný.
Čtení z konzole Pro čtení z konzole lze použít System.in.read(byte b). Jak sami vidíte, pro standardní vstup je použita instance in třídy InputStream, tudíž je možno číst pouze po bytech. Tento standardní vstup, stejně jako výstup, otevírá JVM vždy při spuštění programu. Pro přečtení řádky z konzole je tedy nutné tento stream "zabalit" do filtrů. Nejprve je vhodné převést vstup ze čtení po bytech na čtení po znacích pomocí instance třídy InputStreamReader takto: InputStreamReader ctiZnak = new InputStreamReader(System.in); Pro čtení po řádcích pak tento vstup zabalíme ještě do BufferedReaderu jako při čtení ze souboru. Standardní vstup se zavírá automaticky při skončení programu, není tedy nutné použít metodu close(). Stejně jako při čtení ze souboru musíme ošetřit výjimky IOException. Následující příklad přečte jednu řádku z konzole: import java.io.*; public class Cteni2 { public static void main (String [] args) { System.out.print("Zadej text: "); try { BufferedReader cti =new BufferedReader (new InputStreamReader(System.in)); String s = cti.readLine(); System.out.println (s); } catch (IOException e) { System.out.println("chyba vstupu"); } } }
41
Vstupy a výstupy
Výstupní proudy
OutputStream
PipedOutputStream
FileOutputStream
ByteArrayOutputStream FilterOutputStream
DataOutputStream
PrintStream
BufferedOutputStream obrázek 4 Struktura dědíčnosti třídy OutputStream
Obdobně jako u vstupu lze třídy pro výstup rozdělit do čtyř skupin: třídy pro vytvoření výstupního proudu, třídy pro rozšíření funkčnosti výstupního proudu, třídy pro vytvoření Writeru a třídy pro rozšíření funkčnosti Writeru. Třída OutputStream FileOutputStream PipedOutputStream ByteArrayOutputStream
použití abstraktní třída, která definuje základní metody pro zápis po bytech zápis do souboru, parametrem konstruktoru je String se jménem souboru nebo object typu File zápis do roury (do objektu, ze kterého čte PipedInputStream) zápis do pole bytů v paměti, které je parametrem konstruktoru
tabulka 12 Třídy pro zápis po bytech
Třída FilterOutputStream BufferedOutputStream DataOutputStream PrintStream
použití předek tříd, které přidávají k OutputStreamu další funkčnost vytváří buffer pro efektivnější zápis ve spolupráci s třídou DataInputStream umožňuje přenášet údaje ve formátu přenositelném mezi různými platformami formátuje výstup, poskytuje metody print a println
tabulka 13 Třídy pro přídání funkčnosti pří zápisu po bytech
42
Vstupy a výstupy Třída
použití
abstraktní třída, která definuje základní metody pro zápis po znacích OutputStreamWriter převádí OutputStream na Writer zápis do souboru, parametrem konstruktoru je FileWriter String se jménem souboru nebo object typu File zápis do roury (do objektu, ze kterého čte PipedWriter PipedReader) zápis do bufferu, který může být převeden do StringWriter objektu String či StringBuffer zápis do pole znaků v paměti CharArrayWriter Writer
odpovídající OutputStream OutputStream FileOutputStream PipedOutputStream ByteArrayOutputStream
tabulka 14 Třídy pro zápis po znacích
Třída FilterWrite BufferedWriter PrintWriter
použití předek tříd, které přidávají k OutputStreamu další funkčnost vytváří buffer pro efektivnější zápis formátuje výstup, poskytuje metody print a println
odpovídající OutputStream FilterOutputStream BufferedOutputStream PrintStream
tabulka 15 Třídy pro rozšíření funkčnosti při zápisu po znacích
Třída DataOutputStream nemá odpovídající Writer.
Zápis do textového souboru Pro zápis do textového souboru použijeme zápis po znacích. Nejdříve musíme soubor pro zápis otevřít, tj. vytvořit výstupní writer. Pokud soubor na disku neexistuje, bude vytvořen nový, pokud existuje, bude přepsán. Jestliže chceme do již existujícího souboru připisovat na konec, musíme použít konstruktor třídy FileWriter se dvěma parametry. První parametr je soubor, do kterého budeme zapisovat. Druhý je logická hodnota, která určuje, zda budeme zapisovat za konec souboru nebo původní soubor přepisovat. Hodnota true znamená dopisování na konec souboru. Pro zápis po znacích do souboru A.TXT si můžeme připravit instanci třídy FileWriter takto: FileWriter pisZnak = new FileWriter ("a.txt"); Takto připravený výstup nám umožní zápis po znacích. Výhodnější je však zapisovat po řádcích, proto musíme ještě použít filtr PrintWriter, který má metodu println() pro zápis celého řádku. PrintWriter vystup = new PrintWriter (pisZnak); Po skončení zápisu nesmíme zapomenout na metodu close() pro zavření souboru. Jako u každé práce s vstupy a výstupy je nutné ošetřit výjimky. Následující příklad ukazuje zapsání deseti řádek do textového souboru na disk po řádcích (metodou println()).
43
Vstupy a výstupy import java.io.*; public class Soub { public static void main(String [] args) { try { PrintWriter vystup = new PrintWriter (new FileWriter("a.txt")); for (int i=1; i<11 ; i++) vystup.println("radek "+i); vystup.close(); } catch (IOException e) { System.out.println("Chyba pri zapisu"); } } } Výstup na konzoli (System.out) nemusíme nijak "zabalit" protože autoři Javy použili ve třídě System pro proměnnou out typ PrintOutputStream, tj. třídu která umí zapisovat celé řádky.
Třída StringTokenizer V balíku java.util lze najít třídu StringTokenizer, které je určena pro rozdělování textových řetězců na jednotlivé části. Pokud máme např. textový soubor s adresami, kdy na každém řádku jsou údaje o jedné osobě, potřebujeme při načítání souboru oddělit jednotlivé části jako je jméno, příjmení atd. Právě k tomuto účelu slouží třída StringTokenizer a její metody. V konstruktoru určíme/ řetězec, který se má rozdělit na tzv. tokeny. Je možno zadat i rozdělovací znak. Pokud použijete konstruktor bez určení rozdělovacího znaku, jsou standardně použity znaky "\t\n\r", což je tabulátor, mezera a nový řádek. Třída StringTokenizer poskytuje metodu hasMoreTokens() pro určení, zda ještě následuje další část řetězce (vrací hodnotu true nebo false) a metodu nextToken(), která vrací hodnotu tokenu jako String. V následujícím příkladě je ukázáno použití této třídy pro rozdělení řádky textového souboru na části. Soubor Zvirata.txt má následující obsah: pes Rek kocka Micka kocka Mourek pes Alik morce Smudla morce Fousek kocka Packa pes Bety pes Asta kocka Paty pes Fik V následujícím programu vypíšeme pouze jména zvířat a bez označení druhu.
44
Vstupy a výstupy import java.io.*; import java.util.*; public class Tokenizace { public static void main(String [] args) { try { BufferedReader vstup = new BufferedReader (new FileReader("zvirata.txt")); String s,s1,s2 = " "; while ((s = vstup.readLine()) != null) { StringTokenizer t = new StringTokenizer(s); s1 = t.nextToken(); s2 = t.nextToken(); System.out.println(s2); } vstup.close(); } catch (IOException e) { System.out.println("Chyba pri cteni/zapisu"); } } } Výstup programu je následující: Rek Micka Mourek Alik Smudla Fousek Packa Bety Asta Paty Fik
Další třídy a metody pro práci s proudy Práce se vstupními a výstupními proudy není pouze v balíku java.io, ale i v několika dalších. Existují další metody a třídy pro vytváření vstupních proudů (např. pro čtení ze sítě, pro čtení BLOB z databází) i další třídy pro přidání funkčnosti k proudům (např. komprimace či šifrování).
Čtení ze sítě Nejdříve si zde uvedeme, jak číst soubor ze sítě. Základem je třída URL, v rámci které se zadává adresa souboru, ke kterému chceme přistupovat. Nejjednodušší je zadat textovou adresu jako parametr konstruktoru: URL mojeURL = new URL("http://www.vse.cz/"); Pokud zadáme špatný parametr, vyvolá konstruktor výjimku MalformedURLException. Instance třídy URL může vytvořit vstupní proud následujícím způsobem:
45
Vstupy a výstupy InputStream is = mojeURL.openStream(); S takto vytvořeným proudem můžeme pracovat jako s kterýmkoliv jiným proudem. Následující příklad vypíše soubor na URL http://www.vse.cz/ na standardní výstup (porovnejte ho s prvním příkladem v kapitole o vstupních souborech): import java.io.*; import java.net.*; public class CteniURL { public static void main (String [] args){ try { URL mojeURL = new URL("http://www.vse.cz/”); InputStream is = mojeURL.openStream(); BufferedReader vstup = new BufferedReader (new InputStreamReader (is)); String s; while ((s = vstup.readLine()) != null) System.out.println (s); vstup.close(); } catch (MalformedURLException e) { System.out.println ("Chybne URL"); } catch (IOException e){ System.out.println ("Chyba na vstupu"); } } }
Komprimace a dekomprimace souborů Třídy a metody pro komprimaci a dekomprimaci souborů typu ZIP a GZIP jsou v balíku java.util.zip. Následující příklad zkomprimuje vstupní soubor, jehož jméno je zadáno jako parametr na příkazové řádce do výstupního zkomprimovaného souboru test.gz. import java.io.*; import java.util.zip.*; public class GZIPCompress { public static void main (String [] args){ try { BufferedReader vstup = new BufferedReader ( new FileReader(args[0])); BufferedOutputStream vystup = new BufferedOutputStream ( new GZIPOutputStream ( new FileOutputStream("test.gz"))); int c; while ((c = vstup.read()) != -1) vystup.write(c); vstup.close(); vystup.close(); } catch (IOException e){
46
Vstupy a výstupy System.out.println ("Chyba na vstupu/vystupu"); e.printStackTrace(); } } }
Třída File Třída File slouží pro manipulaci se soubory a adresáři. Instancí třídy File může být adresář nebo soubor. Při vytváření instance není nutné, aby soubor (případně adresář) fyzicky existoval na disku. Pro oddělování adresářů a souborů v popisu cesty používáme lomítko / nebo dvě zpětná lomítka (\\ ve Windows). Lze použít i proměnnou File.separator. Vytvoření instance třídy File je možno pomocí tří různých konstruktorů -
File (String jmeno); File (String cesta, String jmeno); File (File adresar, String jmeno);
příklad použití File mujSoubor = new File ("a.txt")
význam instance mujSoubor nastavena na soubor a.txt v aktuálním adresáři File mojeDopisy = new File("C:"+File.separator+ instance mojeDopisy nastavena na adresář dopisy "dopisy") na disku C File mujDopis = new File ("C:/dopisy/dopis1.txt") instance mujDopis nastavena na soubor dopis1.txt v adresáři dopisy na disku C File mujDopis = new File instance mujDopis nastavena na soubor dopis1.txt ("C:\\dopisy","dopis1.txt") v adresáři dopisy na disku C File dopis = new File (mojeDopisy,"dopis1.txt") instance mujDopis nastavena na soubor dopis1.txt v adresáři dopisy na disku C, použili jsme instanci mojeDopisy vytvořenou dříve tabulka 16 Použití konstruktorů třídy File
Třída File obsahuje metody isFile() a isDirectory(), které zjistí, zda daná instance třídy File je soubor či adresář (musí již fyzicky existovat na disku). Pokud chceme existenci souboru nebo adresáře ověřit použijeme metodu exists(). Všechny tyto tři metody vracejí hodnotu typu boolean. Pro vytvoření adresáře slouží metoda mkdir(), pro vytvoření souboru metoda createNewFile() Zjistit velikost existujícího souboru nebo adresářemůžeme pomocí metody length().Soubor nebo adresář je možno smazat metodou delete() nebo přejmenovat pomoci metody renameTo(). Pro výpis adresáře slouží metoda list(), která vrací pole řetězců se jmény souborů a adresářů. Následující program vypíše obsah adresáře dokumenty na disku C. import java.io.*; public class VypisAdresare { public static void main (String [] args) { File adresar = new File ("C:/dokumenty"); String [] obsah = adresar.list(); for (int i = 0;i < obsah.length;i++) System.out.println(obsah[i]); } }
47
Vstupy a výstupy Nyní program upravíme tak, aby vypsal pouze adresáře obsažené v adresáři dokumenty. import java.io.*; public class VypisAdresare2 { public static void main (String [] args) { File adresar = new File ("C:/dokumenty"); String [] obsah = adresar.list(); for (int i = 0;i < obsah.length;i++){ File prvek = new File (adresar,obsah[i]); if (prvek.isDirectory()) System.out.println(obsah[i]); } } } Pokud chceme použít pro výpis masku (tj. vypsat pouze některé soubory či adresáře na základě nějaké podmínky) použijeme jako parametr metody list instanci třídy, která má implementováno rozhraní FilenameFilter. Toto rozhraní má pouze jednu metodu boolean accept ( File dir, String name ), kde dir je adresář, který obsahuje testovaný soubor a name je jméno tohoto souboru. Metoda vrací logickou hodnotu zda soubor vyhovuje výběrovému kritériu.
48
Dynamické datové struktury
7. Dynamické datové struktury Java poskytuje několik možností pro uložení většího množství dat (tj. objektů či primitivních datových typů) v paměti. S nejjednodušší z nich, s polem, jsme se již seznámili. Pole je struktura velmi jednoduchá na používání, která má následující výhody: •
pole je ze všech datových struktur nejefektivnější z hlediska ukládání a vybírání dat, pokud však potřebuje provádět složitější operace (např. zajistit, aby prvek byl vložen pouze jednou), může být použití jiných struktur efektivnější,
•
pole zajišťuje typovou kontrolu – pokud nadefinujete pole objektů určité třídy, můžete do něho vkládat pouze instance této třídy (a instance potomků této třídy),
•
pole umožňuje vkládat přímo primitivní datové typy, u ostatních datových struktur se musí převést na objekty.
Pole má však také dvě velké nevýhody: •
je třeba předem znát počet prvků, které do něj budete ukládat,
•
nepodporuje některé složitější způsoby práce s objekty (např. vkládání prvků doprostřed pole či vytváření asociativních polí).
Java poskytuje pro práci s poli pomocnou třídu Arrays, která nabízí např. třídění pole či prohledávání pole.
Kontejnery Při ukládání objektů do dynamických datových struktur (často se pro ně používá pojem kontejner) nemusíme dopředu znát počet vkládaných objektů. Jednotlivé typy kontejnerů dále umožňují složitější operace s daty. Ve zbytku této kapitoly se budeme zabývat kontejnery implementovanými v Javě verze 1.2. Starší verze Javy poskytují jiné dynamické struktury (Vector, Stack, HashTable) – tyto zde nebudeme popisovat. Na následujícím obrázku vidíme strukturu dědění tříd včetně zakomponovaných struktur ze starších verzí, na dalším pak už zjednodušenou strukturu pro nové třídy verze 1.2. Tečkovaně jsou znázorněna rozhraní, čárkovaně abstraktní třídy a plnou čarou třídy.
49
Dynamické datové struktury Produces
Iterator
Produces
Collection
Map
AbstractMap
Produces
ListIterator
List
Set SortedMap
AbstractCollection
SortedSet HashMap
AbstractList
TreeMap
AbstractSet WeakHashMap
HashSet
TreeSet
Hashtable (Legacy) Utilities
Vector (Legacy)
Collections ArrayList
AbstractSequentialList Arrays LinkedList
Stack (Legacy)
Comparable
Comparator
obrázek 5 Vazby mezi třídami balíku java.util Iterator
Collection
Map
Produces
Produces
ListIterator Produces
HashMap List
TreeMap
Set WeakHashMap
ArrayList
LinkedList
HashSet
TreeSet
Utilities Collections
Comparable
Comparator
Arrays
obrázek 6 Vazby mezi třídami balíku java.util (pouze pro nové třídy z Java 1.2)
Na první pohled vypadá tato struktura velmi složitě, ale po podrobnějším zkoumání zjistíte, že jde vlastně jen o dva druhy tříd – kolekce (Collection) a mapy (Map). Rozhraní Collection poskytuje základní metody pro tvorbu a použití seznamů (List) a množin (Set). Seznamy ukládají instance do lineární struktury s opakováním prvků. Obvykle se používá ArrayList, pouze v případě velkého množství vkládání/vyjímání dovnitř/zevnitř seznamu je výhodnější LinkedList.
50
Dynamické datové struktury Množiny ukládají pouze instance různých hodnot – pro zjištění odlišnosti se používá metoda equals (v praxi se do množiny vkládají instance pouze jedné třídy). Efektivnější je použití varianty HashSet, pouze v případě, že potřebujeme mít seznam hodnot trvale setříděn, používá se TreeSet. Mapy (používá se též pojem asociativní pole) ukládají dvojice instancí - klíč a jemu odpovídající hodnotu. Klíče se v Map nemohou opakovat (obdobně jako v množině). Efektivnější je použití varianty HashMap, varianta TreeMap zajišťuje trvalé setřídění mapy dle klíčů. Rozhraní Iterator umožňuje procházet jednotlivé prvky kolekce bez ohledu na její konkrétní typ (např. když chcete vypsat všechny prvky kolekce). Pokud chceme vkládat instance do struktur, které udržují setříděné prvky (TreeSet a TreeMap), je nutné zajistit porovnání těchto instancí na větší/menší. Toto lze zajistit dvěmi způsoby – buď musí mít vkládané instance implementováno rozhraní Comparable s metodou compareTo nebo při vytvoření dynamické struktury je nutné zadat implementaci rozhraní Comparator, která umí porovnat vkládané objekty. Třída Collections (neplést s rozhraním Collection) poskytuje další metody pro práci s kontejnery jako je např. třídění, synchronizace datové struktury či „zmražení“ datové struktury. V následujících tabulkách najdete přehled nejpoužívanějších metod jednotlivých rozhraní Rozhraní Collection Metoda
užití
boolean add(Object v)
Přidá do kolekce prvek typu Object. Pokud se operace nepodaří vrací false.
void clear( )
Zruší všechny prvky v kolekci.
boolean contains(Object v)
Vrací true, jestliže kolekce obsahuje prvek uvedený jako argument metody.
boolean isEmpty( )
Vrací true, jestliže je kolekce prázdná.
Iterator iterator( )
Vrací Iterator, pomocí kterého lze projít všechny prvky kolekce.
boolean remove(Object v)
Jestliže kolekce obsahuje argument, je odpovídající prvek zrušen. Vrací true když je prvek zrušen.
int size( )
Vrací počet prvků uložených v kolekci.
tabulka 17 Metody rozhraní Collection
51
Dynamické datové struktury Rozhraní Map metoda
užití
boolean
Pokud je klíč obsažen v mapě, vrací true.
containsKey(Object k) void clear( )
Zruší všechny prvky v mapě.
boolean
Vrací true, jestliže mapa obsahuje hodnotu uvedenou jako
containsValue(Object v)
argument metody.
boolean isEmpty( )
Vrací true, jestliže je mapa prázdná.
Collection value( )
Vrací kolekci (Collection) hodnot.
Set keySet( )
Vrací množinu (Set) obsahující klíče.
Set entrySet( )
Vrací množinu (Set) s prvky Map.Entry.
Object put(Object k, Object v)
Uloží hodnoty do mapy.
int size( )
Vrací počet prvků uložených v mapě.
Object get(Object k)
Vrací hodnotu odpovídající zadanému klíči.
tabulka 18 Metody rozhraní Map
Rozhraní Iterator Metoda
užití
boolean hasNext( )
Vrací true, pokud je v kontejneru další prvek.
Object next( )
Vrátí následující prvek kontejneru.
tabulka 19 Metody rozhraní Iterator
Vytvoření kontejneru Všechny kontejnery jsou definovány tak, že jejich prvky jsou instance třídy Object a instance potomků třídy Object (což znamená, že do kontejnerů lze vkládat instance libovolných tříd, neboť v Javě jsou všechny třídy potomky třídy Object). Z toho také vyplývá jedno omezení kontejnerů, nelze do nich ukládat primitivní datové typy - pokud je potřebujeme do kontejneru vložit, musíme je převést do objektové podoby, tedy např. celá čísla na instance třídy Integer. Nejjednodušší způsob vytvoření a vypsání několika kontejnerů je vidět v následujícím příkladě. import java.util.*; public class VypisKontejneru { static Collection napln(Collection c) { c.add("pes"); c.add("pes"); c.add("kocka"); return c; }
52
Dynamické datové struktury static Map napln(Map m) { m.put("pes", "Alik"); m.put("pes", "Rek"); m.put("kocka", "Mina"); return m; } public static void main(String[] args) { System.out.println(napln(new ArrayList())); System.out.println(napln(new HashSet())); System.out.println(napln(new HashMap())); } } Výsledkem tohoto programu je následující výpis: [pes, pes, kocka] [kocka, pes] {kocka=Mina, pes=Rek} Na prvním řádku je výstup z listu, proto se může stejný řetězec opakovat vícekrát. Na druhém řádku je výpis setu a tedy každý prvek má jedinečnou hodnotu. Pro výpis mapy na třetím řádku platí totéž, klíč, v tomto případě se řetězec "pes", se vyskytuje pouze jednou a to s hodnotou "Rek", která byla vkládána jako druhá. Z toho vyplývá, že v případě vložení klíče, který už v map je, je původní hodnota přepsána novou. Tedy v našem příkladě "Alik" je přepsáno řetězcem "Rek". Pokud potřebujeme do kontejneru vložit čísla, tak jednoduché řešení ukazuje následující příklad: ArrayList cisla = new ArrayList(); for(int i = 0; i < 10 i++) cisla.add(new Integer(i)); Jestliže potřebujeme vytvořit mapu, ve které budou hodnoty také číselné a při každém dalším výskytu stejného klíče se budou měnit, máme dvě možnosti řešení. První je uložit jako hodnotu objektovou reprezentaci čísla a při každé změně jeho hodnoty převádět objekt na jednoduchou proměnnou, provést změnu a znovu převést na objekt (tento způsob je použit v souhrnném příkladě na konci kapitoly). Druhá možnost řešení je vytvořit si vlastní třídu s proměnnou jednoduchého typu a její instance využít jako hodnoty v mapě, viz následující příklad. V něm se vygeneruje 10000 náhodných celých čísel z intervalu 1 až 20 a zjišťuje se, kolikrát bylo které vygenerováno. Vygenerované číslo je klíčem v mapě (převedené na instance třídy Integer), počty výskytů se počítají pomocí vlastní třídy Counter, jejíž instance jsou hodnoty v mapě. import java.util.*; class Counter { int pocet = 1; public String toString() { return Integer.toString(pocet); } }
53
Dynamické datové struktury public class Statistika { public static void main(String[] args) { HashMap hm = new HashMap(); for(int i = 0; i < 10000; i++) { Integer r = new Integer((int)(Math.random() * 20)); if(hm.containsKey(r)) ((Counter)hm.get(r)).pocet++; else hm.put(r, new Counter()); } System.out.println(hm); }} Všimněte si, že ve třídě Counter je definována metoda toString, která se automaticky použije pro výpis hodnoty konkrétní instance v metodě System.out.println.
Procházení kontejneru - Iterátor Pro postupné procházení jednotlivých seznamů se používají metody rozhraní Iterator. Rozhraní Collection poskytuje metodu iterator( ), která vytvoří instanci iterátoru. Pomocí metody next( ) vrací Iterator jednotlivé hodnoty (nejdříve první, poté postupně následující). Metoda hasNext( ) zjišťuje, zda následuje další prvek. Pro následující příklady této kapitoly si definujeme dvě jednoduché třídy public class Kocka { private int cisloKocky; Kocka(int i) { cisloKocky = i; } void tisk() { System.out.println("Kocka #" + cisloKocky); }} public class Pes { private int cisloPsa; Pes(int i) { cisloPsa = i; } void tisk() { System.out.println("Pes #" + cisloPsa); }} V následujícím příkladě si ukážeme, jak vytvořit a naplnit ArrayList a jak jeho obsah vypsat pomocí iterátoru. import java.util.*; public class Kocky { public static void main(String[] args) { Collection kocky = new ArrayList(); for(int i = 0; i < 7; i++) kocky.add(new Kocka(i)); Iterator it = kocky.iterator(); while(it.hasNext()) ((Kocka)it.next()).tisk(); } }
54
Dynamické datové struktury Všimněte si deklarace kolekce kocky – pokud nepotřebujeme v programu specifické vlastnosti konkrétní kolekce, obvykle se deklaruje jako typ Collection. Tento postup umožňuje jednoduchou výměnu typu kolekce, pokud by nevyhovovala. Při výpisu je použito přetypování (Kocka)e.next(), protože jinak není možné použít metodu tisk() třídy Kocka. Po vyjmutí z kolekce nebo mapy je prvek instancí třídy Object a teprve přetypováním zajistíme přiřazení vyjmuté instance ke správné třídě. V tomto se ale může skrývat mnoho chyb, které se projeví až při spuštění programu viz následující příklad. import java.util.*; public class KockyAPsi { public static void main(String[] args) { List kocky = new ArrayList(); for(int i = 0; i < 7; i++) kocky.add(new Kocka(i)); //není problem pridat do listu i psa (i on je //potomkem tridy Object) kocky.add(new Pes(7)); for(int i = 0; i < kocky.size(); i++) ((Kocka)kocky.get(i)).tisk(); } } Na rozdíl od minulého příkladu, kdy pro procházení seznamu byl použit Iterator, zde byla pro procházení seznamu použita metoda get(int i), která vrací hodnotu prvku na zadané pozici v seznamu (a je dostupná pouze pro seznamy – List). Instanci třídy lze přetypovat na instanci kteréhokoli z jeho předků nikoli na libovolný typ. Nelze tedy z instance třídy Pes udělat instanci třídy Kocka – pokud se o to pokusíte (toto nastane ve výše uvedeném příkladu), vznikne výjimka ClassCastException. Java nám poskytuje operátor instanceOf, pomocí kterého lze zjistit, zda instance je instancí zadané třídy. V následujícím příkladě použijeme tento operátor k rozdělení instancí vyjmutých z listu kocky, do kterého uložíme instance tříd Kocka a Pes.
import java.util.*; public class KockyAPsi2 { Collection zvirata = new ArrayList(); KockyAPsi2() { for (int i =1;i<11;i++) { zvirata.add(new Kocka(i)); zvirata.add(new Pes(i)); } }
55
Dynamické datové struktury void vypis(){ Iterator it = zvirata.iterator(); Object o; while (it.hasNext()) { if ((o=it.next()) instanceof Kocka) ((Kocka)o).tisk(); else ((Pes)o).tisk(); } } public static void main (String [] args) { KockyAPsi2 kockyAPsi = new KockyAPsi2(); kockyAPsi.vypis(); } } Pro výpis hodnot z mapy nelze přímo použít iterátor, ale je nutné převést mapu na kolekci. Rozhraní Map má k tomuto účelu tři metody – metodu value(), která vytvoří kolekci hodnot, metodu keySet(), která vytvoří množinu klíčů a metodu entrySet( ), pomocí které lze vytvořit z mapy množinu objektů MapEntry obsahujících klíč i příslušnou hodnotu. Pomocí metod getKey( ) a getValue( ) pak můžeme získat jednotlivé prvky mapy. Následující metoda vypisMapy (TreeMap tm) ukazuje možné použití těchto metod pro vypsání obsahu mapy. void vypisMapy (TreeMap tm) { Iterator it = tm.entrySet().iterator(); while(it.hasNext()) { Map.Entry a = (Map.Entry)it.next(); System.out.println(a.getKey()+"/t/t"+a.getValue); } }
Souhrnný příklad Zadání příkladu: Máme soubor Zvirata.txt: pes Rek kocka Micka kocka Mourek pes Alik morce Smudla morce Fousek kocka Packa pes Bety pes Asta kocka Paty pes Fik Našim úkolem je zjistit, kolik je v souboru záznamů o jednotlivých druzích zvířat. Z každé věty tedy potřebujeme jen její první část, proto použijeme StringTokenizer. Při tvorbě mapy budeme postupovat následovně: pomocí metody containsKey() zjistíme, zda už je tato hodnota klíče v mapě uložena. Pokud ano, pomocí metody get() získáme hodnotu odpovídající tomuto klíči a převedeme ji na číslo,
56
Dynamické datové struktury přičteme 1 a hodnotu znovu uložíme do mapy. Pokud se klíč ještě v mapě nevyskytuje, uložíme ho do ní s hodnotou Integer(1). Výsledky pomocí iterátoru vypíšeme. import java.util.*; import java.io.*; public class TvorbaMapy { public static HashMap vytvor(String jmenoSouboru) { LinkedList ln = new LinkedList(); HashMap tm = new HashMap(); try { BufferedReader veta = new BufferedReader(new FileReader(jmenoSouboru)); String s,s1,s2 = " "; while ((s = veta.readLine()) != null){ StringTokenizer t = new StringTokenizer(s); s1 = t.nextToken(); s2= t.nextToken(); if (tm.containsKey(s1)) { int i = ((Integer)tm.get(s1)).intValue(); tm.put(s1,new Integer(++i)); } else { tm.put(s1, new Integer(1)); }; } veta.close(); } catch (FileNotFoundException e1) { System.out.println("Soubor nenalezen"); } catch (IOException e2 ) { System.out.println("Chyba pri cteni"); } return tm; } public static void tiskMapy( HashMap m){ Iterator it = m.entrySet().iterator(); while(it.hasNext()) { Map.Entry a = (Map.Entry)it.next(); System.out.println(a.getKey()+"\t"+a.getValue()); } } public static void main (String []args) { tiskMapy(vytvor("zvirata.txt")); } }
57
Grafické uživatelské rozhraní
8. Grafické uživatelské rozhraní Až dosud jsme pro výstupy a vstupy do našich programů využívali pouze konzoli nebo soubor. Java nám však poskytuje nástroje pro tvorbu grafického uživatelského rozhraní, tj. "okének", menu, tlačítek atd. Už od první verze Javy je její součástí balík java.awt (abstract window toolkit) poskytující třídy pro tvorbu rozhraní a od verze 1.2 také balík (package) javax.swing. V následujících příkladech budeme využívat především třídy z balíku swing, používání tříd z AWT je velmi podobné.
První aplikace Pokud chceme vytvořit aplikaci s grafickým uživatelským rozhraním, prvním krokem je vytvořit rámec („okno“) ve kterém aplikace poběží. V jazyce Java se používá třída JFrame (třída Frame v awt). Následující program ukazuje, jak vytvořit základní rámec aplikace a na obrázku vidíte výsledek. import java.awt.event.*; import javax.swing.*; public class PrvniAplikace extends JFrame { class Wl extends WindowAdapter { public void windowClosing (WindowEvent e) { System.exit(0); } }; public PrvniAplikace(String nazev) { super(nazev); addWindowListener (new Wl()); } public static void main (String []args) { PrvniAplikace apl=new PrvniAplikace("Moje prvni aplikace"); apl.setLocation(100,100); apl.setSize(200,200); apl.setVisible(true); } } Třída PrvniAplikace je potomkem třídy JFrame a obsahuje dvě metody: konstruktor PrvniAplikace a metodu main. Dále v našem programu vytvoříme vnitřní třídu Wl, která je potomkem třídy WindowAdapter a bude obsluhovat události okna (tj. uživatelské akce jako je zmenšení na ikonu, změna velikosti okna nebo zavření okna). Zde je definováno, že při uzavření okna má skončit aplikace – tj. předefinujeme metodu windowClosing() předka, která se volá při uzavření okna.
58
Grafické uživatelské rozhraní Ošetření ostatních události, které vznikají u okna (tzv. WindowEvent) nepřekrýváme – tj. bude se používat standardní ošetření nadefinované v předkovi. Všimněte si, že třída Wl je nadefinována uvnitř třídy PrvniAplikace – třídy vytvořené uvnitř jiných tříd se nazývají vnitřní třídy (inner class). V konstruktoru pomocí vyvolání konstruktoru předka s parametrem tj. příkaz super (String s) přiřadíme našemu oknu titulek. Další část konstruktoru, metoda addWindowListener, zajistí, že naše okno bude reagovat na obsluhu uživatele prostřednictvím třídy Wl (tj. teprve nyní se přiřadí obsluha popsaná ve třídě Wl k našemu oknu). V metodě main pak vytvoříme instanci třídy PrvniAplikace s názvem "Moje první aplikace", nastavíme jí výchozí umístění na obrazovce (metoda setLocation), výchozí velikost (metoda setSize) a zobrazíme ji na obrazovce (metoda setVisible). Většina RAD nástrojů pro Javu používá pro popis zavření okna ješte o něco složitější konstrukci s využitím anonymní vnitřní třídy. Následující program je stejný jako předchozí, pouze pro obsluhu událostí okna používá anonymní vnitřní třídu. Tato konstrukce bude používána ve všech dalších programech. import java.awt.event.*; import javax.swing.*; public class PrvniAplikace2 extends JFrame { public PrvniAplikace2(String nazev) { super(nazev); addWindowListener(new WindowAdapter () { public void windowClosing (WindowEvent e) { System.exit(0); } }); } public static void main (String [] args) { PrvniAplikace2 apl=new PrvniAplikace2("Moje prvni aplikace"); apl.setLocation(100,100); apl.setSize(200,200); apl.setVisible(true); } }
Vkládání dalších komponent Na následující příkladě si ukážeme, jak do okna aplikace přidat další prvky. Zadání je jednoduché, přidat do aplikace posuvník a vypisovat, na jakou hodnotu je právě nastaven. Použijeme tedy instanci třídy JScrollBar a JLabel.
59
Grafické uživatelské rozhraní import java.awt.event.*; import javax.swing.*; import java.awt.*; public class PrvniAplikace3 extends JFrame { private int hodnota = 0; //Definice a vytvoření instancí posuvníku a návěstí. private JScrollBar posuvnik = new JScrollBar (JScrollBar.HORIZONTAL,0,1,0,100); private JLabel napis = new JLabel ("Vybrana hodnota je:"+hodnota); public PrvniAplikace3(String nazev) { super(nazev); addWindowListener(new WindowAdapter () { public void windowClosing (WindowEvent e) { System.exit(0); } }); getContentPane().add(posuvnik,BorderLayout.NORTH); getContentPane().add(napis,BorderLayout.SOUTH); } public static void main (String []args) { PrvniAplikace3 apl=new PrvniAplikace3("Moje prvni aplikace"); apl.setLocation(100,100); apl.setSize(200,200); apl.setVisible(true); } }
Jak vidíme, základ aplikace zůstal stejný, metoda main se vůbec nezměnila. Použité komponenty (posuvník JScrollBar a popiska JLabel) jsou definovány jako proměnné instance této třídy. Dále definujeme proměnnou hodnota pro předávání hodnoty nastavené na posuvníku. Popisku a posuvník umístíme do okna pomocí metody add. (Knihovna Swing vyžaduje, aby všechny vkládané komponenty byly umístěny do “podokna” rámce – podokno získáte voláním metody getContentPane() – bližší popis je v dokumentaci na java.sun.com). V metodě add se pomocí druhého parametru specifikuje rozmístění komponent v okně v rámci aktuálního správce rozvržení. Posuvník je inicializován jako vodorovně orientovaný, nastavený na hodnotu nula, velikost posunovače je 1, minimální hodnota 0 a maximální 100 (protože posunovač má hodnotu 1, je skutečná nejvyšší nastavitelná hodnota 99, při velikosti posunovače 10 by byla 90 atd.). Tato aplikace sice zobrazí posuvník a nápis, ale na obsluhu posuvníku nereaguje. Je nutné obsloužit události posuvníku.
60
Grafické uživatelské rozhraní
Obsluha událostí Do naší aplikace je tedy nutné přidat obsluhu událostí generovaných instancí třídy JScrollBar. Upravený kód vypadá následovně a vzhled aplikace ukazuje obrázek import java.awt.event.*; import javax.swing.*; import java.awt.*; public class PrvniAplikace4 extends JFrame { private JScrollBar posuvnik; private JLabel napis; private int hodnota = 0; private class Al implements AdjustmentListener { public void adjustmentValueChanged(AdjustmentEvent e) { hodnota = posuvnik.getValue(); napis.setText("Vybrana hodnota je: "+hodnota); } }; public PrvniAplikace4(String nazev) { super(nazev); addWindowListener(new WindowAdapter () { public void windowClosing (WindowEvent e) { System.exit(0); } }); posuvnik= new JScrollBar(JScrollBar.HORIZONTAL,0,1,0,100); posuvnik.addAdjustmentListener (new Al()); napis = new JLabel ("Vybrana hodnota je: "+hodnota); getContentPane().add(posuvnik,BorderLayout.NORTH); getContentPane().add(napis,BorderLayout.SOUTH); } public static void main (String []args) { PrvniAplikace4 apl= new PrvniAplikace4("Moje prvni aplikace"); apl.setLocation(100,100); apl.setSize(200,200); apl.setVisible(true); } } Do naší aplikace jsme přidali třídu Al implementující rozhraní AdjustmentListener, která definuje metodu adjustmentValueChanged(AdjustmentEvent e). Tato metoda má jako parametr AdjustmentEvent, tj. událost, kterou generují instance třídy JScrollBar. V rámci obsluhy události posuvníku pomocí metody getValue( ) získáme hodnotu nastavenou na posuvníku a změníme text zobrazovaný popiskou pomocí metody setText(String s). Ani toto však
61
Grafické uživatelské rozhraní ještě nestačí, posledním krokem je přiřadit v konstruktoru tento listener konkrétní instanci třídy JScrollBar pomocí metody addAdjustmentListener( ). Obecně lze tedy říci, že komponentu, kterou chceme přidat do okna aplikace, nejprve deklarujeme jako datový atribut instance potomka třídy JFrame a přiřadíme mu počáteční hodnotu, tj. vytvoříme instanci komponenty pomocí konstruktoru. Pak definujeme třídu implementující rozhraní (případně třídu, která je potomkem příslušného XxxAdapteru) pro obsluhu událostí této komponenty. Posledním krokem je přiřazení konkrétního "posluchače událostí" konkrétní komponentě pomocí metody addXxxListener(). Následující tabulky by vám měly umožnit základní orientaci v komponentách, událostech, rozhraních pro sledování událostí atd. V tabulce je přehled všech událostí, rozhraní určených pro jejich obsluhu a metod pro přiřazení a odejmutí těchto ovladačů, v druhém sloupci naleznete seznam komponent, které generují tyto události.
Událost, rozhraní a add- a remove-metody ActionEvent ActionListener addActionListener( ) removeActionListener( ) AdjustmentEvent AdjustmentListener addAdjustmentListener( ) removeAdjustmentListener( ) ComponentEvent ComponentListener addComponentListener( ) removeComponentListener( ) ContainerEvent ContainerListener addContainerListener( ) removeContainerListener( ) FocusEvent FocusListener addFocusListener( ) removeFocusListener( ) KeyEvent KeyListener addKeyListener( ) removeKeyListener( ) MouseEvent MouseListener addMouseListener( ) removeMouseListener( )
62
Komponenty sledující tyto události
JButton, JList, JTextField, JMenuItem a jeho potomci včetně JCheckBoxMenuItem, JMenu, and JPopupMenu
JScrollbar Třída Component a její potomci,včetně tříd JButton, JCanvas, JCheckbox, JComboBox, Container, JPanel, JApplet, JScrollPane, Window, JDialog, JFileChooser, JFrame, JLabel, JList, JScrollbar, JTextArea, a JTextField Třída Container a její potomci, včetně tříd JPanel, JApplet, JScrollPane, Window, JDialog, JFileChooser, a JFrame Třída Component a její potomci,včetně tříd JButton, JCanvas, JCheckbox, JComboBox, Container, JPanel, JApplet, JScrollPane, Window, JDialog, JFileChooser, JFrame, JLabel, JList, JScrollbar, JTextArea, a JTextField Třída Component a její potomci,včetně tříd JButton, JCanvas, JCheckbox, JComboBox, Container, JPanel, JApplet, JScrollPane, Window, JDialog, JFileChooser, JFrame, JLabel, JList, JScrollbar, JTextArea, a JTextField Třída Component a její potomci,včetně tříd JButton, JCanvas, JCheckbox, JComboBox, Container, JPanel, JApplet, JScrollPane, Window, JDialog, JFileChooser, JFrame, JLabel, JList, JScrollbar, JTextArea, a JTextField
Grafické uživatelské rozhraní MouseEvent MouseMotionListener addMouseMotionListener( ) removeMouseMotionListener( ) WindowEvent WindowListener addWindowListener( ) removeWindowListener( ) ItemEvent ItemListener addItemListener( ) removeItemListener( ) TextEvent TextListener addTextListener( ) removeTextListener( )
Třída Component a její potomci,včetně tříd JButton, JCanvas, JCheckbox, JComboBox, Container, JPanel, JApplet, JScrollPane, Window, JDialog, JFileChooser, JFrame, JLabel, JList, JScrollbar, JTextArea, a JTextField Window a jeho potomci JDialog, JFileChooser, a JFrame
JCheckBox, JCheckBoxMenuItem, JComboBox, JList
Potomci JTextComponent, včetně JTextArea a JTextField
tabulka 20 Vztah událostí a komponent
Následující tabulka obsahuje přehled metod definovaných v jednotlivých rozhraních pro sledování událostí. Pokud má rozhraní více jak jednu metodu, existuje třída implementující toto rozhraní standardním způsobem (jmenuje se vždy XxxAdapter, kde Xxx je shodné s první částí pojmenování Listeneru).
Listener - rozhraní a adapter - třída ActionListener AdjustmentListener ComponentListener ComponentAdapter ContainerListener ContainerAdapter FocusListener FocusAdapter KeyListener KeyAdapter
MouseListener MouseAdapter MouseMotionListener MouseMotionAdapter
Metody v rozhraní actionPerformed(ActionEvent) adjustmentValueChanged(AdjustmentEvent) componentHidden(ComponentEvent) componentShown(ComponentEvent) componentMoved(ComponentEvent) componentResized(ComponentEvent) componentAdded(ContainerEvent) componentRemoved(ContainerEvent) focusGained(FocusEvent) focusLost(FocusEvent) keyPressed(KeyEvent) keyReleased(KeyEvent) keyTyped(KeyEvent) mouseClicked(MouseEvent) mouseEntered(MouseEvent) mouseExited(MouseEvent) mousePressed(MouseEvent) mouseReleased(MouseEvent) mouseDragged(MouseEvent) mouseMoved(MouseEvent)
63
Grafické uživatelské rozhraní
WindowListener WindowAdapter
ItemListener TextListener
windowOpened(WindowEvent) windowClosing(WindowEvent) windowClosed(WindowEvent) windowActivated(WindowEvent) windowDeactivated(WindowEvent) windowIconified(WindowEvent) windowDeiconified(WindowEvent) itemStateChanged(ItemEvent) textValueChanged(TextEvent)
tabulka 21 Listenery a jejich metody
Rozložení komponent v rámci okna V našem úvodním programu pro grafické rozhraní jsme použili několik konstrukcí, které ještě nebyly vysvětleny. Objevily se zde dva řádky kódu: getContentPane().add(posuvnik,BorderLayout.NORTH); getContentPane().add(napis,BorderLayout.SOUTH); Zatím jsme si vysvětlili, že metoda add() slouží pro přidání komponenty do rámce, ale ne kam budou komponenty umístěny. Pro určení rozmístění komponent se používají správci rozložení (tzv. layout manager). Základní správce rozvržení si nyní popíšeme.
Rozložení BorderLayout Toto rozložení je standardně nastaveným rozložením pro okno aplikace. Z tohoto důvodu jsme v úvodním příkladě žádné rozložení nenastavovali a jen jsme jej používali. BorderLayout rozděluje okno aplikace do pěti oblastí (viz obrázek), přičemž do každé z nich lze vložit pouze jednu komponentu (může to být instance třídy JPanel, do které lze vložit další komponenty). Velikost komponenty závisí na oblasti do které je vložena. Oblasti NORTH, SOUTH, WEST a EAST jsou jen tak velké, aby bylo možno zobrazit komponentu do nich vloženou. Oblast CENTER pak zabírá celý zbytek okna a komponenta do ní vložená je roztažena na celou tuto oblast. V úvodním příkladu jsme umístili posuvník do oblasti North a návěstí do oblasti South.
Rozložení FlowLayout Rozložení FlowLayout, jak již název napovídá, je velmi volné a uspořádání komponent závisí na velikosti okna. Komponenty se rovnají do řádků v pořadí v jakém je metodou add přidáváte do okna. Následující obrázky ukazují dva různé vzhledy téže aplikace v závislosti na tom, jak si uživatel nastaví velikost okna.
64
Grafické uživatelské rozhraní
Rozložení GridLayout Toto rozložení vytváří tabulku o zadaném počtu řádků a sloupců. Komponenty mají maximální velikost, při které se do okna ještě vejdou. Zvětší-li uživatel okno, zvětší se komponenty. Následující obrázek ukazuje aplikaci s GridLayoutem nastaveným na 3 řádky a 2 sloupce. Komponenty jsou řazeny do tabulky podle pořadí jejich vložení do okna metodou add a jejich velikost je určena velikostí okna (mají maximální velikost, při které se vejdou do okna).
Další rozvržení V předcházejících podkapitolách jsme si ukázali pouze některé správce rozvržení. Dalším zajímavým rozložením s největší možností nastavení je GridBagLayout. Toto rozvržení je však příliš složité. Pro jednoduché aplikace stačí kombinace předcházejících a pro složitější aplikace pravděpodobně použijete RAD prostředí, které použití layoutu vyřeší za vás. Dalším rozvržením je CardLayout, pro vytvoření tzv. karet. Ale toto rozložení neposkytuje možnost vytvořit kartičky s oušky, přepínání mezi kartičkami musíme řešit pomocí tlačítek nebo rozbalovacího seznamu. Balík javax.swig nám poskytuje třídu JTabbedPane, která tyto problémy řeší za nás.
Kombinování správců rozvržení Jednotlivá rozvržení lze v rámci jedné aplikace kombinovat. Pro kombinování se používají instance třídy JPanel. Tato třída má stejně jako třída JFrame metody add() a setLayout(), standardním rozvržením pro panel je ale FlowLayout. Tato komponenta však nemá žádné viditelné ohraničení a lze ji vložit do instance třídy JFrame nebo opět do instance třídy JPanel. Příklad kombinování layoutů najdete např. v podkapitole Textová oblast.
65
Grafické uživatelské rozhraní
Nastavení vlastností komponent Pro každou komponentu je možné nastavit několik obecných vlastností pomocí metod jejich společného předka – třídy Component. Některé z nich vidíte v tabulce.
metoda třídy Component Color getBackground() void setBackground(Color) Color getForeground() void setForeground(Color) Font getFont() void setFont(Font) Dimension size() void setVisible(boolean) boolean isVisible() void setEnabled (boolean) boolean isEnabled ()
popis metody Vrátí nastavení barvy pozadí jako instanci třídy Color. Nastaví barvu pozadí komponenty. Vrátí nastavení barvy popředí jako instanci třídy Color. Nastaví barvu popředí komponenty. Vrátí informace o nastaveném fontu. Nastaví font. Vrátí aktuální velikost komponenty. Lze použít i metody size().width() a size().height() pro získání každého rozměru zvlášť. Skryje nebo zobrazí komponentu na obrazovce. Vrátí true nebo false podle nastavení viditelnosti. Zpřístupní či znepřístupní komponentu, při znepřístupnění je komponenta viditelná, ale uživatel ji nemůže použít. Zjistí stav zpřístupnění
tabulka 22 Vybrané metody třídy Component
Pro komponenty ze swingu existuje i metoda setToolTipText(String s), pomocí které můžeme pro komponentu nastavit plovoucí nápovědu. Použití těchto metod si ukážeme v následujících kapitolách.
Třída Font Pro nastavení vzhledu písma slouží třída Font z knihovny AWT, kterou lze použít ve swingu. Umožňuje nastavit vzhled písma, řez písma a velikost písma. Pro Javu je definováno 5 logických fontů, jejichž existence je zaručena na všech platformách. Pro jednotlivé platformy je pak v souboru font.properties uloženo, které fonty reprezentují (soubor font.properties je uložen v adresáři jre/lib). V následující tabulce jsou uvedena jména logických fontů a jejich reprezentace ve Windows. Logický font
odpovídající font ve Windows
Serif
Times New Roman
Sans-serif
Arial
Monospaced
Curier New
Dialog
Arial
DialogInput
Curier New
tabulka 23 Logické fonty Javy a jejich nastavení ve Windows
V konstruktoru fontu je možné použít i název fontu z platformy, na které pracujete, tedy např. Arial nebo Georgia. Přenositelnost na jiné platformy je však poté problematická. Pro nastavení řezu písma má třída Font tři konstanty: PLANT, BOLD (tučné) a ITALIC (kurzíva).
66
Grafické uživatelské rozhraní Konstruktor třídy Font má následující tvar: Font(String name, int style, int size). Chceme-li tedy definovat pro náš program písmo s logickým fontem Dialog, tučným a o velikosti 20, vytvoříme ho takto: Font f1 = new Font ("Dialog",Font.BOLD,20). Font s písmem Georgia (pokud tento font máte ve svém operačním systému), tučnou kurzívou a velikostí 14 vytvoříme takto: Font f2 = new Font ("Georgia",Font.BOLD + Font.ITALIC,14)
Třída Color Třída Color slouží k nadefinování barvy. Najdeme v ní několik konstant, které můžeme použít. Pokud např. chceme v aplikaci použít modré tlačítko, nadefinujeme ho pomocí konstanty takto: JButton tlacitko = new JButton ("Tlacitko"); tlacitko.setBackground (Color.blue); Třída Color má také několik konstruktorů pro "namíchání" vlastních barev. Nejčastěji využijete dva následující: Color (int r, int g, int b), kde každý z parametrů je z intervalu 0 až 255 Color (int rgb) Bílou barvu tedy můžeme "namíchat" takto: Color barva1 = new Color(255,255,255); Color barva2 = new Color(0xFF,0xFF,0xFF); Color barva3 = new Color(0xFFFFFF);
Tlačítka a textové pole Knihovna Swing nám poskytuje několik různých tlačítek, nejprve si ukážeme "obyčejné" tlačítko, tj. instanci třídy JButton. V následujícím programu použijeme dvě tlačítka a textové pole. Po stisknutí tlačítka se v textovém poli zobrazí text, který je na tlačítku. V konstruktoru tlačítka nastavíme text, který bude na tlačítku zobrazen, v konstruktoru textového pole (třída JTextField) nastavíme výchozí velikost (počet znaků, které lze zapsat). Použijeme FlowLayout a umístíme komponenty. Pro tlačítka musíme napsat ovladače a nastavit je jako "posluchače událostí". Tlačítko po stisknutí generuje událost ActionEvent, je tedy nutné napsat vnitřní třídu AkceTlacitko, která implementuje rozhraní ActionListener. ActionListener obsahuje jedinou metodu actionPerformed (ActionEvent e), ve které musíme popsat činnost po stisknutí tlačítka. Pomocí metody události e.getSource() získáme zdroj události. Víme, že je to tlačítko, a tak ho přetypujeme (JButton)e.getSource(). Nakonec použijeme metodu getText(), která nám vrátí text zobrazovaný na tlačítku. Pomocí metody instance třídy JTextField setText() zobrazíme tento text v textovém poli. Pro každé tlačítko můžeme napsat jinou implementaci ActionListeneru. V našem příkladě je činnost obou tlačítek shodná, stačí tedy pouze jeden ovladač přiřazený oběma tlačítkům.
67
Grafické uživatelské rozhraní
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class TlacitkaATextPole extends JFrame { JButton tlacitko1 = new JButton("Tlacitko 1"), tlacitko2 = new JButton("Tlacitko 2"); JTextField txt = new JTextField(10); class AkceTlacitko implements ActionListener { public void actionPerformed (ActionEvent e) { String jmenoTlacitka=((JButton)e.getSource()).getText(); txt.setText(jmenoTlacitka); } }; public TlacitkaATextPole (String nadpis) { super (nadpis); addWindowListener(new WindowAdapter () { public void windowClosing (WindowEvent e) { System.exit(0); } }); getContentPane().setLayout(new FlowLayout()); tlacitko1.addActionListener(new AkceTlacitko()); tlacitko2.addActionListener(new AkceTlacitko()); getContentPane().add(tlacitko1); getContentPane().add(tlacitko2); getContentPane().add(txt); }; public static void main (String args []) { TlacitkaATextPole apl = new TlacitkaATextPole("Aplikace s tlacitky"); apl.setSize(200,100); apl.setVisible(true); } }
Textová oblast V minulém příkladě jsme si ukázali textové pole, v tomto použijeme textovou oblast, v knihovně swing je to třída JTextArea. Textová oblast je vlastně víceřádkovým textovým polem a tudíž mnohé metody jsou stejné. Navíc obsahuje metodu append() pro přidávání textu za text, který už byl do textové oblasti vložen.
68
Grafické uživatelské rozhraní Našim úkolem je vytvořit program, který po zadání jména souboru zobrazí obsah tohoto souboru v textové oblasti. Horní řádek je složen z popisky (instance JLabel) a vstupního pole pro jméno souboru (instance JTextField). Obojí je vloženo do panelu (instance JPanel) – je to ukázka kombinování správců rozvržení (celé okno používá rozložení BorderLayout, v rámci panelu je použito rozložení FlowLayout). Obsah souboru se zobrazí v textové oblasti vytvořené instancí třídy JTextArea. Vzhledem k tomu, že obsah souboru může být větší než okno, je textová oblast „obalena“ posuvníkem (JScrollPane), který umožňuje posouvat se po souboru, který je větší než okno. Další novinkou v tomto příkladě je obsluha JTextField – po zapsání jména souboru a stisknutí klávesy <Enter> chceme vypsat obsah souboru. Musíme tedy reagovat na události z klávesnice. Proto vytvoříme vnitřní třídu, která je potomkem třídy KeyAdapter a naimplementujeme metodu keyPressed (KeyEvent e). Událost KeyEvent se generuje při každém stisknutí klávesy – proto musíme zjistit, jaká klávesa byla stisknuta (metoda getKeyChar() ) a pouze v případě stisku klávesy <Enter> (konstanta KeyEvent.VK_ENTER) provedeme příslušnou akci, tj. zjistíme jméno souboru a spustíme výpis. Při jakékoli jiné klávese se neprovádíme nic (po stisknutí další klávesy se samozřejmě spustí obsluha automaticky znovu). Metoda zpracovani otevře soubor, připraví oblast pro výpis (nastaví barvu textu na černou a vymaže předchozí výpis, lze vypisovat postupně několik souborů) a pomocí metody append přidává do textové oblasti jednotlivé řádky souboru. Pokud zadáte jméno neexistujícího souboru je odchycena výjimka a červeně zobrazeno chybové hlášení. Na závěr zpracování je uzavřen soubor.
69
Grafické uživatelské rozhraní import import import import
javax.swing.*; java.awt.*; java.awt.event.*; java.io.*;
public class TextovaOblast extends JFrame { JPanel zahlavi = new JPanel(); JLabel textZadej = new JLabel ("Zadej soubor: "); JTextField zadejSoubor = new JTextField(20); JTextArea vypis = new JTextArea(); class Kl extends KeyAdapter { public void keyPressed (KeyEvent e) { if (e.getKeyChar()==KeyEvent.VK_ENTER) { zpracovani(zadejSoubor.getText()); zadejSoubor.setText(""); } } }; void zpracovani(String jmenoSouboru) { try { BufferedReader veta = new BufferedReader(new FileReader(jmenoSouboru)); String s = ""; vypis.setForeground(Color.black); vypis.setText(""); while ((s = veta.readLine()) != null){ vypis.append(s+"\n"); } veta.close(); this.setTitle(jmenoSouboru); } catch (FileNotFoundException e) { vypis.setForeground(Color.red); vypis.setText("Soubor nebyl nalezen!!!!"); } catch (IOException e ) { System.out.println("Chyba pri cteni"); } } public TextovaOblast(String nadpis){ super(nadpis); addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); zahlavi.add(textZadej); zahlavi.add(zadejSoubor); getContentPane().add(zahlavi,BorderLayout.NORTH); getContentPane().add(new JScrollPane(vypis)); zadejSoubor.addKeyListener(new Kl()); }
70
Grafické uživatelské rozhraní public static void main(String[] args) { TextovaOblast apl = new TextovaOblast( "Aplikace s textovou oblasti"); apl.setLocation(100, 100); apl.setSize(400, 400); apl.setVisible(true); } }
Zaškrtávací políčka a přepínače Pro zaškrtávací políčka a přepínače nám knihovna swing poskytuje dvě třídy a to JCheckBox (zaškrtávací čtvereček) a JRadioButton (zaškrtávací kolečko). Instance obou těchto tříd lze použít k oběma účelům. Jestliže potřebujeme přepínače (tj. lze označit pouze jednu možnost ze skupiny), musíme použít instanci třídy ButtonGroup a jednotlivé možnosti do ní vložit. Úkolem následujícího programu je pomocí zaškrtávacích políček nastavit vzhled písma v textovém poli. Nastavujeme kurzívu nebo tučné písmo (je tedy možné zaškrtnout jednu variantu, obě nebo žádnou) a barvu písma (je třeba vybrat právě jednu možnost). Pro barvy musíme tedy použít sloučení tlačítek do skupiny. Připravíme si tedy čtyři instance třídy JCheckBox pro barvy, v konstruktoru určíme text u políčka. Obdobně vytvoříme dvě instance třídy JRadioButton. Připravíme si také fonty pro nastavování, skupinu tlačítek, panel pro umístění tlačítek a text pro nastavování vlastností. V konstruktoru aplikace nastavíme správce rozmístění pro aplikaci a správce rozmístění pro panel tlačítek (použijeme GridLayout(1,6), tedy tabulkové rozložení s jedním řádkem a šesti sloupci). Do panelu a do skupiny tlačítek přidáme metodou add tlačítka pro volbu barev, do panelu přidáme i tlačítka pro volbu vzhledu písma. U tlačítek pro barvu určíme pomocí metody setSelected(), které bude zaškrtnuté při spuštění aplikace. U tlačítek pro vzhled písma neoznačíme žádné a nastavíme odpovídající font. Instance obou druhů tlačítek generují při zaškrtnutí instanci třídy ActionEvent, pro ovládání napíšeme proto vnitřní třídy implementující rozhraní ActionListener. Pro každou barvu napíšeme vlastní ovladač, pro vzhled písma také jeden, abychom mohli zjistit kombinace zaškrtnutí. Barvu textu v textové oblasti nastavíme pomocí metody setForeground() a font pomocí metody setFont(). Jestli je zaškrtávací políčko zaškrtnuto či ne zjistíme pomocí metody isSelected().
71
Grafické uživatelské rozhraní import javax.swing.*; import java.awt.*; import java.awt.event.*; public class RuznaTlacitka extends JFrame { JCheckBox tucne = new JCheckBox ("Tučně"); JCheckBox kurziva = new JCheckBox ("Kurzíva"); JRadioButton cerna = new JRadioButton("Černá"); JRadioButton cervena = new JRadioButton("Červená"); JRadioButton modra = new JRadioButton("Modrá"); JRadioButton zelena = new JRadioButton("Zelená"); ButtonGroup skupinaTlacitek = new ButtonGroup(); JPanel panelTlacitek = new JPanel(); JTextField text = new JTextField("Zkušební text"); Font f1 = new Font ("Dialog",Font.PLAIN,20); Font f2 = new Font ("Dialog",Font.BOLD,20); Font f3 = new Font ("Dialog",Font.ITALIC,20); Font f4 = new Font ("Dialog",Font.BOLD+Font.ITALIC,20); class NastavModrou implements ActionListener { public void actionPerformed (ActionEvent e) { text.setForeground(Color.blue); } }; class NastavCernou implements ActionListener { public void actionPerformed (ActionEvent e) { text.setForeground(Color.black); } }; class NastavCervenou implements ActionListener { public void actionPerformed (ActionEvent e) { text.setForeground(Color.red); } }; class NastavZelenou implements ActionListener { public void actionPerformed (ActionEvent e) { text.setForeground(Color.green); } }; class ZmenaPisma implements ActionListener { public void actionPerformed(ActionEvent e){ if(tucne.isSelected() && kurziva.isSelected()) text.setFont(f4); if(tucne.isSelected() && (!kurziva.isSelected())) text.setFont(f2); if((!tucne.isSelected()) && kurziva.isSelected()) text.setFont(f3); if((!tucne.isSelected()) && (!kurziva.isSelected())) text.setFont(f1); } };
72
Grafické uživatelské rozhraní public RuznaTlacitka(String nadpis) { super (nadpis); addWindowListener(new WindowAdapter () { public void windowClosing (WindowEvent e) { System.exit(0); } }); panelTlacitek.setLayout(new GridLayout(1,6)); getContentPane().setLayout(new BorderLayout()); skupinaTlacitek.add(cerna); skupinaTlacitek.add(cervena); skupinaTlacitek.add(modra); skupinaTlacitek.add(zelena); panelTlacitek.add(cerna); panelTlacitek.add(cervena); panelTlacitek.add(modra); panelTlacitek.add(zelena); panelTlacitek.add(tucne); panelTlacitek.add(kurziva); modra.addActionListener(new NastavModrou()); cerna.addActionListener(new NastavCernou()); cervena.addActionListener(new NastavCervenou()); zelena.addActionListener(new NastavZelenou()); tucne.addActionListener(new ZmenaPisma()); kurziva.addActionListener(new ZmenaPisma()); cerna.setSelected(true); text.setFont(f1); getContentPane().add( panelTlacitek,BorderLayout.NORTH); getContentPane().add(new JScrollPane(text)); } public static void main (String args[]) { RuznaTlacitka a = new RuznaTlacitka("Aplikace s tlačítky"); a.setLocation(100,100); a.setSize(400,200); a.setVisible(true); } }
Rozbalovací seznam Další komponenta, jejíž použití si ukážeme, je rozbalovací seznam, pro který má knihovna swing třídu JComboBox. JComboBox umožňuje vytvořit seznam více položek a vybrat z nich právě jednu. Nabídka seznamu se rozvine po stisknutí ovládací šipky. Následující program umožní změnit barvu pozadí aplikace podle výběru z rozbalovacího seznamu. Třída JComboBox má konstruktor, který nastaví jednotlivé možnosti nabídky z pole řetězců. Proto si připravíme pole s jednotlivými texty nabídek a pole s odpovídajícími barvami. 73
Grafické uživatelské rozhraní Vytvoříme instanci třídy JComboBox a přidáme ji do okna aplikace a nastavíme barvu pozadí podle seznamu (pokud neurčíte jinak bude vybrána první možnost). Také instance třídy JComboBox generují ActionEvent. Je tedy třeba napsat ovladač implementující ActionListener a přiřadit ho rozbalovacímu seznamu pomocí addActionListener. V ovladači je třeba zjistit, která volba byla vybrána, pomocí metody getSelectedIndex(). Tato metoda vrací index (pořadí) vybrané volby, první volba má index 0. Na základě indexu zvolíme položku z pole barev a použijeme ji k nastavení barvy aplikace. Odkaz na aplikaci se zapíše pomocí konstrukce RozbalovaciSeznam.this. Pouhé this nestačí, neboť jsme uvnitř vnitřní třídy a this tu označuje tuto vnitřní třídu. Nakonec musíme aplikaci znovu zobrazit s novou barvou. import java.awt.event.*; import javax.swing.*; import java.awt.*; public class RozbalovaciSeznam extends JFrame { String []barvy = {"lightgray","black","blue", "cyan","gray","green","red","white"}; Color [] konstanty = {Color.lightGray,Color.black, Color.blue,Color.cyan,Color.gray, Color.green,Color.red,Color.white}; JComboBox vyber = new JComboBox(barvy); class Al implements ActionListener { public void actionPerformed (ActionEvent e){ int index = vyber.getSelectedIndex(); RozbalovaciSeznam.this.getContentPane(). setBackground(konstanty[index]); RozbalovaciSeznam.this.setVisible(true); } } RozbalovaciSeznam (String nazev){ super(nazev); addWindowListener(new WindowAdapter () { public void windowClosing (WindowEvent e) { System.exit(0); } }); getContentPane().add(vyber,BorderLayout.NORTH); vyber.addActionListener(new Al()); vyber.setSelectedIndex(5); int index = vyber.getSelectedIndex(); this.getContentPane().setBackground(konstanty[index]); }
74
Grafické uživatelské rozhraní public static void main (String []args) { RozbalovaciSeznam apl = new RozbalovaciSeznam("Aplikace s rozbalovacim seznamen"); apl.setLocation(100,100); apl.setSize(180,250); apl.setVisible(true); } }
Menu Pro tvorbu menu nám knihovna swing poskytuje řadu tříd. Třída JMenuBar umožňuje vytvořit prázdnou lištu pro menu v horní části aplikace (bez závislosti na správci rozvržení). Pro tvorbu položek menu, které obsahují další položky, slouží třída JMenu. Pro vytvoření položek menu, které již vyvolají nějakou činnost programu, slouží třída JMenuItem. Třídy JCheckBoxMenuItem a JRadioButtonMenuItem nám poskytují možnost vytvořit zaškrtávací položky v menu – podobně jako u tlačítek se po zařazení do skupiny mohou chovat jako přepínače. Vytvoříme program s menu, v horní liště budou volby Soubor, Formát a Nápověda. Volba Soubor bude obsahovat volby Otevřít, Uložit a Konec. Volba Nápověda volbu O aplikaci. Ve volbě Formát budou dvě zaškrtávací volby pro tučné písmo a kurzívu (mohou být zvoleny obě, jedna nebo nic) a další menu Barvy. Menu Barvy bude obsahovat čtyři zaškrtávací volby fungující jako přepínače (tj. musí být zvolena právě jedna možnost). Jinak bude aplikace obsahovat pouze textové pole se zkušebním textem pro ověření funkce některých nabídek. Na začátku si tedy musíme připravit jednotlivé položky menu, textové pole a fonty pro nastavování. Pro přepínání barev je nutné vytvořit skupinu tlačítek (instanci třídy ButtonGroup). V konstruktoru musíme nastavit instanci třídy JMenuBar jako lištu naší aplikace pomocí metody setJMenuBar(). Pro každou položku menu je možné pomocí metody setMnemonic(KeyEvent.klávesa) nastavit možnost aktivovat položku menu stisknutím klávesy ALT a podtrženého písmene v menu. Konstanty pro jednotlivé klávesy najdete v nápovědě u třídy KeyEvent s balíku awt.event. Pomocí metody add() pak vytvoříme jednotlivá menu, položky vkládáme v pořadí, v jakém mají být zobrazeny. Pokud chceme mezi jednotlivé položky vložit oddělovač, použijeme metodu addSeparator(). Pokud chceme pro některou položku, která je instanci JMenuItem (tj. spouští činnost neotevírá jiné menu), nastavit kombinaci kláves pro provedení akce (např. pro otevření souboru CTRL+O), použijeme metodu setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,ActionEvent.CTRL_MASK)).
75
Grafické uživatelské rozhraní Pokud mají volby pro barvy fungovat jako přepínače, musíme je pomocí metody add() vložit také do instance třídy ButtonGroup. Je třeba přiřadit a napsat odpovídající ovladače. Instance tříd JMenuItem a JRadioMenuItem při zvolení nabídky generují události typu ActionEvent a instance třídy JCheckBoxMenuItem události ItemEvent. Pro obsluhu událostí je třeba napsat ovladače implementující odpovídající rozhraní. Rozhraní ActioListener má metodu actionPerformed(ActionEvent e) a rozhraní ItemListener metodu itemStateChanged(ItemEvent e). Nastavení barev a fontů pak provedeme stejným způsobem jako v kapitole o zaškrtávacích polích. Ovladače pro soubory a nápovědu si popíšeme v následujících kapitolách (zdrojový text je obsahuje) import javax.swing.*; import java.awt.*; import java.awt.event.*; public class RuznaMenu extends JFrame { JMenuBar lista = new JMenuBar(); JMenu soubor = new JMenu("Soubor"); JMenu format = new JMenu("Formát"); JMenu napoveda = new JMenu ("Nápověda"); JMenu barvy = new JMenu("Barvy"); JMenuItem otevrit = new JMenuItem("Otevřít"); JMenuItem ulozit = new JMenuItem("Uložit"); JMenuItem konec = new JMenuItem("Konec"); JMenuItem oAplikaci = new JMenuItem ("O aplikaci"); JCheckBoxMenuItem tucne = new JCheckBoxMenuItem("Tučné"); JCheckBoxMenuItem kurziva = new JCheckBoxMenuItem("Kurzíva"); JRadioButtonMenuItem modra = new JRadioButtonMenuItem("Modrá"); JRadioButtonMenuItem cerna = new JRadioButtonMenuItem("Černá"); JRadioButtonMenuItem cervena = new JRadioButtonMenuItem("Červená"); JRadioButtonMenuItem zelena = new JRadioButtonMenuItem("Zelená"); ButtonGroup skupina = new ButtonGroup(); JTextField text = new JTextField("Zkušební text"); JTextArea souborAAdresar = new JTextArea(); Font f1 = new Font("DialogInput",Font.PLAIN,20); Font f2 = new Font ("DialogInput",Font.BOLD,20); Font f3 = new Font ("DialogInput",Font.ITALIC,20); Font f4 = new Font ("DialogInput",Font.BOLD+Font.ITALIC,20); class VyberSouboru implements ActionListener { public void actionPerformed (ActionEvent e) { JFileChooser fc = new JFileChooser(); int stavVyberu = fc.showOpenDialog(RuznaMenu.this); if (stavVyberu ==JFileChooser.APPROVE_OPTION){ souborAAdresar.setText("Vybrany soubor: " +fc.getSelectedFile().getName()+"\n");
76
Grafické uživatelské rozhraní souborAAdresar.append("Vybrany adresar: " +fc.getCurrentDirectory().toString()); } else souborAAdresar.setText( "Akce vyberu souboru stornovana"); RuznaMenu.this.repaint(); } }; class UlozeniSouboru implements ActionListener { public void actionPerformed (ActionEvent e) { JFileChooser fc = new JFileChooser(); fc.showSaveDialog(RuznaMenu.this); RuznaMenu.this.repaint(); } }; class Ukonceni implements ActionListener { public void actionPerformed (ActionEvent e) { System.exit(0); } }; class MujItemAdapter implements ItemListener { public void itemStateChanged(ItemEvent e){ if(tucne.isSelected() && kurziva.isSelected()) text.setFont(f4); if(tucne.isSelected() && (!kurziva.isSelected())) text.setFont(f2); if((!tucne.isSelected()) && kurziva.isSelected()) text.setFont(f3); if((!tucne.isSelected()) && (!kurziva.isSelected())) text.setFont(f1); } }; class NastavModrou implements ActionListener { public void actionPerformed (ActionEvent e) { text.setForeground(Color.blue); } }; class NastavCernou implements ActionListener { public void actionPerformed (ActionEvent e) { text.setForeground(Color.black); } }; class NastavCervenou implements ActionListener { public void actionPerformed (ActionEvent e) { text.setForeground(Color.red); } };
77
Grafické uživatelské rozhraní class NastavZelenou implements ActionListener { public void actionPerformed (ActionEvent e) { text.setForeground(Color.green); } }; class OtevreniDialogu implements ActionListener { public void actionPerformed (ActionEvent e) { JOptionPane.showMessageDialog(null, "informace o programu", "O Programu", JOptionPane.INFORMATION_MESSAGE); } }; public RuznaMenu(String nadpis){ super(nadpis); addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); setJMenuBar(lista); soubor.setMnemonic(KeyEvent.VK_S); format.setMnemonic(KeyEvent.VK_F); napoveda.setMnemonic(KeyEvent.VK_N); lista.add(soubor); lista.add(format); lista.add(napoveda); soubor.add(otevrit); otevrit.setAccelerator(KeyStroke.getKeyStroke( KeyEvent.VK_O,ActionEvent.CTRL_MASK)); otevrit.addActionListener(new VyberSouboru()); soubor.add(ulozit); ulozit.addActionListener(new UlozeniSouboru()); soubor.addSeparator(); soubor.add(konec); konec.addActionListener(new Ukonceni()); MujItemAdapter mia = new MujItemAdapter(); tucne.addItemListener(mia); kurziva.addItemListener(mia); format.add(tucne); format.add(kurziva); modra.addActionListener(new NastavModrou()); cerna.addActionListener(new NastavCernou()); cervena.addActionListener(new NastavCervenou()); zelena.addActionListener(new NastavZelenou()); cerna.setSelected(true); format.addSeparator(); format.add(barvy); barvy.add(modra); skupina.add(modra); barvy.add(cerna); skupina.add(cerna); barvy.add(cervena); skupina.add(cervena);
78
Grafické uživatelské rozhraní barvy.add(zelena); skupina.add(zelena); napoveda.add(oAplikaci); oAplikaci.addActionListener(new OtevreniDialogu()); getContentPane().add(text); getContentPane().add(souborAAdresar, BorderLayout.SOUTH); text.setFont(f1); } public static void main(String[] args) { RuznaMenu apl = new RuznaMenu("Aplikace s menu"); apl.setLocation(100, 100); apl.setSize(400, 400); apl.setVisible(true); } }
Okna pro otevírání a ukládání souborů Pro otevírání a ukládání souboru poskytuje knihovna swing třídu JFileChooser. V minulém programu s menu byly volby Soubor-Otevrit a Soubor-Ulozit. Pro jejich obsluhu byly napsány ovladače implementující ActionListener. V jejich metodě actionPerformed je popsáno základní použití oken pro otevírání a ukládání souborů. Vytvoříme tedy instanci třídy JFileChooser a pomocí metody showOpenDialog() či showSaveDialog() otevřeme příslušné okno. Jako parametr vložíme odkaz na aplikaci, která okno otevírá (opět je nutná konstrukce jménonadřízenétřídy.this, pouhé this odkazuje na ovladač, ne na aplikaci). Do proměnné stavVyberu se vrátí informace o tom, jak uživatel dialog ukončil. Pokud je vrácena hodnota odpovídající konstantě APPROVE_OPTION, uživatel vybral soubor, pokud se rovná konstantě CANCEL_OPTION, uživatel akci stornoval. Pomocí metody getSelectedFile() získáme uživatelem vybraný soubor (jako instanci třídy File) metoda getCurrentDirectory() vrací vybraný adresář (jako instanci třídy File). Pokud nebyl soubor nebo adresář vybrán vracejí metody hodnotu null. Toto okno je standardně nastaveno jako modální tj. uživatel musí reagovat na toto okno, hlavní okno aplikace je neaktivní a nelze se do něj přepnout. V ovladači našeho programu pouze zobrazujeme jméno vybraného souboru a adresáře. class VyberSouboru implements ActionListener { public void actionPerformed (ActionEvent e) { JFileChooser fc = new JFileChooser(); int stavVyberu = fc.showOpenDialog(RuznaMenu.this); if (stavVyberu ==JFileChooser.APPROVE_OPTION){ souborAAdresar.setText("Vybrany soubor: "+fc.getSelectedFile().getName()+"\n"); souborAAdresar.append("Vybrany adresar: "+fc.getCurrentDirectory().toString()); }
79
Grafické uživatelské rozhraní else souborAAdresar.setText("Akce vyberu souboru stornovana"); RuznaMenu.this.repaint(); } };
Informační okna V kapitole o menu jsme použili volbu O aplikaci. Po jejím vybrání se zobrazí jednoduché informační okno. Pro jeho vytvoření jsme použili třídu JOptionPane a její metodu showMessageDialog, která jako parametry používá odkaz na rodiče (může být i prázdný), text, který bude zobrazen v okně, titulek okna a konstantu označující ikonu, která se zobrazí. Na obrázku vidíte výsledné okno naší aplikace a následuje kód, který toto okno definuje.
JOptionPane.showMessageDialog(null, "informace o programu", "O Programu", JOptionPane.INFORMATION_MESSAGE); Třída JOptionPane umožňuje vytvářet i složitější informační okna. Poskytuje 4 metody pro jejich konfiguraci: showConfirmDialog – poskytuje uživateli možnost vybrat jednu z variant typu yes/no/cancel. showInputDialog – prompt pro vstup showMessageDialog – sděluje uživateli zprávu (viz náš příklad) showOptionDialog – kombinace předcházejících možností Všechny zde uvedené metody jsou několikrát přetíženy a umožňují vytvořit i informační okna v češtině, protože i nápisy na tlačítkách jsou parametrizovány. V této třídě je také definováno 5 konstant pro ikony, které se mohou v informačním okně zobrazit (INFORMATION_MESSAGE, ERROR_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE, PLAIN_MESSAGE). Ukážeme si jak vytvořit a obsloužit okno poskytující možnosti volby ano/ne/zrušit. Použijeme metodu showOptionDialog, prvním parametrem je odkaz na rodičovské okno. Dalšími parametry jsou text uvnitř okna a text v záhlaví okna. Pak následuje konstanta určující zda jde o dialog se dvěma tlačítky (hodnota YES_NO_OPTION) nebo se třemi tlačítky (YES_NO_CANCEL_OPTION). Další parametr určuje použitou ikonu. Další parametr je typu icon a umožňuje použít jiný obrázek než předdefinované ikony, pokud použijeme
80
Grafické uživatelské rozhraní standardní uvedeme prázdnou hodnotu null. Předposlední parametr určuje pole prvků typu Object, ze kterého se berou texty na tlačítka. Pokud pole neosahuje stringy ale instance jiné třídy použije se metoda toString. Poslední parametr určuje, která volba má přidělen fokus (tj. je nastavena jako aktivní) při otevření okna. Po uzavření okna do proměnné navrat získáme hodnotu tlačítka, které uživatel stisknul. Pro návratové hodnoty jsou ve třídě JOptionPane definovány konstanty YES_OPTION, NO_OPTION, CANCEL_OPTION, CLOSED_OPTION a OK_OPTION. V našem příkladě jen vypíšeme výběr v okně. Tuto část kódu si můžete vyzkoušet tak, že třídu OtevreniDialogu v příkladě u menu nahradíte tou následující.
class OtevreniDialogu implements ActionListener { public void actionPerformed (ActionEvent e) { Object[] moznosti = {"Ano","Ne","Zrušit"}; int navrat = JOptionPane.showOptionDialog(RuznaMenu.this, "Uložit soubor?", "Dotaz", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, moznosti, moznosti[0]); if (navrat == JOptionPane.YES_OPTION) text.setText("vybrano Ano"); if (navrat == JOptionPane.NO_OPTION) text.setText("vybrano Ne"); if (navrat == JOptionPane.CANCEL_OPTION || navrat == JOptionPane.CLOSED_OPTION) text.setText("akce zrušena "); } };
Kartičky Jak už bylo zmíněno v kapitole o rozložení, poskytuje package javax.swing třídu JTabbedPane pro tvorbu kartiček. Obměníme příklad s tlačítky a tlačítka pro nastavení řezu písma umístíme na jednu kartičku a tlačítka pro barvy na druhou. Kartičky se zobrazí po volbě z menu. Kartičky vytvoříme pomocí konstruktoru JTabbedPane(). Jednotlivé záložky pak vytvoříme pomocí metody addTab(String napisNaZalozce ,JComponent c), první parametr udává nápis na záložce, druhý komponentu do kartičky umístěnou. Chceme-li na jednu kartičku umístit více prvků, použijeme panel. Do panelu umístíme komponenty a panel pak vložíme do kartičky. Podle počtu použití metody addTab se vytvoří odpovídající počet záložek. Aktivní kartičku tj. kartičku, která je při zobrazení v popředí, určíme
81
Grafické uživatelské rozhraní pomocí metody setSelectedIndex(int poradi), kde parametr určuje index karty. Karty jsou indexovány podle pořadí vytvoření metodou addTab, první karta má index 0. import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Karticky extends JFrame { JMenuBar mb = new JMenuBar(); JMenuItem m = new JMenuItem("Formát"); JLabel napis1 = new JLabel ("prvni karticka"); JCheckBox tucne = new JCheckBox ("Tučne"); JCheckBox kurziva = new JCheckBox ("Kurzíva"); JPanel panel1 = new JPanel(); Font f1 = new Font("Dialog",Font.PLAIN,20); Font f2 = new Font ("Dialog",Font.BOLD,20); Font f3 = new Font ("Dialog",Font.ITALIC,20); Font f4 = new Font ("Dialog",Font.BOLD+Font.ITALIC,20); JLabel napis2 = new JLabel ("druha karticka"); JRadioButton cerna = new JRadioButton("Černá"); JRadioButton cervena = new JRadioButton("Červená"); JRadioButton modra = new JRadioButton("Modrá"); JRadioButton zelena = new JRadioButton("Zelená"); ButtonGroup skupinaTlacitek = new ButtonGroup(); JPanel panel2 = new JPanel(); JTabbedPane tabbedPane = new JTabbedPane(); JTextField text = new JTextField("Zkusebni text"); class NastavModrou implements ActionListener { public void actionPerformed (ActionEvent e) { text.setForeground(Color.blue); } }; class NastavCernou implements ActionListener { public void actionPerformed (ActionEvent e) { text.setForeground(Color.black); } }; class NastavCervenou implements ActionListener { public void actionPerformed (ActionEvent e) { text.setForeground(Color.red); } }; class NastavZelenou implements ActionListener { public void actionPerformed (ActionEvent e) { text.setForeground(Color.green); } };
82
Grafické uživatelské rozhraní class ZmenaPisma implements ActionListener { public void actionPerformed(ActionEvent e){ if(tucne.isSelected() && kurziva.isSelected()) text.setFont(f4); if(tucne.isSelected() && (!kurziva.isSelected())) text.setFont(f2); if((!tucne.isSelected()) && kurziva.isSelected()) text.setFont(f3); if((!tucne.isSelected()) && (!kurziva.isSelected())) text.setFont(f1); } }; class ZobrazKarticky implements ActionListener { public void actionPerformed (ActionEvent e) { Karticky.this.getContentPane().add(tabbedPane, BorderLayout.CENTER); Karticky.this.setVisible(true); } }; public Karticky(String nazev) { super(nazev); addWindowListener(new WindowAdapter () { public void windowClosing (WindowEvent e) { System.exit(0); } }); setJMenuBar(mb); mb.add(m); m.addActionListener(new ZobrazKarticky()); panel1.setLayout(new FlowLayout()); tucne.addActionListener(new ZmenaPisma()); kurziva.addActionListener(new ZmenaPisma()); panel1.add(napis1); panel1.add(tucne); panel1.add(kurziva); tabbedPane.addTab("fonty",panel1); tabbedPane.setSelectedIndex(0); panel2.setLayout(new FlowLayout()); skupinaTlacitek.add(cerna); skupinaTlacitek.add(cervena); skupinaTlacitek.add(modra); skupinaTlacitek.add(zelena); panel2.add(napis2); panel2.add(cerna); panel2.add(cervena); panel2.add(modra); panel2.add(zelena); modra.addActionListener(new NastavModrou()); cerna.addActionListener(new NastavCernou()); cervena.addActionListener(new NastavCervenou()); zelena.addActionListener(new NastavZelenou()); cerna.setSelected(true); tabbedPane.addTab("barvy",panel2);
83
Grafické uživatelské rozhraní text.setFont(f1); getContentPane().add(text,BorderLayout.NORTH); } public static void main (String []args) { Karticky apl = new Karticky("Aplikace s kartičkami"); apl.setLocation(100,100); apl.setSize(200,200); apl.setVisible(true); }}
Další komponenty GUI Ukázali jsme si použití pouze některých komponent, které nám Java v knihovnách AWT a swing nabízí. Další zajímavé komponenty jsou např. •
JToolBar – pro tvorbu panelů nástrojů
•
JProgressBar – pro sledování průběhu akce např. načítání souboru
•
JDialog – pro vytvoření dalšího okna v aplikaci s libovolným obsahem
Pro tvorbu rozsáhlého uživatelského rozhraní je výhodné použít tzv. RAD (rapid aplication development) nástroje jako jsou pro Javu např. NetBeans, Forte, JBuilder nebo Visual Java.
84
Applety
9. Applety Již v úvodu jsme si řekli, že kromě aplikací lze vytvářet i applety - programy napsané v Javě a spouštěné v rámci HTML kódu stránky na internetovém prohlížeči. Standardní prohlížeče umějí většinou spouštět applety napsané ve starší verzi Javy 1.1, tj. takové, které nepoužívají grafickou knihovnu swing, ale pouze awt. Pro spuštění appletu napsaného v Javě 1.3 je třeba doinstalovat buď celé vývojové prostředí pro Javu či aspoň prostředí pro běh programů v Javě (Java Runtime Environment). Pokud prohlížeč nemá naimplementovanou Javu verze 1.3, nelze v html kódu stránky použít standardní tag pro applet, ale musíme použít mnohem složitější konstrukci, kterou najdete na stránkách firmy Sun (platí to např. pro Internet Explorer a Netscape Communicator).
Třídy Applet a JApplet Když vytváříme applet, musí program splňovat několik podmínek. Musí být tvořen třídou, která je potomkem třídy Applet z package java.applet nebo potomkem třídy JApplet z package javax.swing. Na rozdíl od aplikace nemusí tato třída implementovat metodu main (ale může, je možno vytvořit program spustitelný jako applet i jako aplikace). Applet přeložíme a v rámci HTML kódu stránky v tagu <APPLET> sdělíme prohlížeči, který soubor class má stáhnout a spustit. Prohlížeč standardně spouští jednotlivé metody třídy Applet v závislosti na akcích uživatele. Přepsáním těchto metod zajistíme funkčnost našeho appletu. V tabulce č. 24 jsou uvedeny některé metody tříd JApplet (dědí tyto metody od třídy Applet), Applet a Container (předek Appletu) a mechanismus jejich spouštění a funkce. Metoda
Automatické Funkce spouštění public void init ( ) ano Metoda init je vyvolána při načtení stránky do prohlížeče, měla by obsahovat akce, které chceme provést pouze jednou. public void start ( ) ano Metoda je vyvolána vždy, když se uživatel vrátí na stránku s appletem. public void stop ( ) ano Metoda je vyvolána vždy, když uživatel přejde na jinou stránku. public void destroy ( ) ano Metoda je vyvolána, když uživatel zavírá stránku s appletem. public String getParameter(String name) ne Metoda pro předání parametru z tagu <APPLET>. public void paint ano Spouští se vždy, když uživatel provede (Graphics g) nějakou akci (např. změní velikost okna). Vykreslí applet. public Component add (Component comp) ne Přidá potomka třídy Component do appletu, např. tlačítko. tabulka 24 Metody třídy Applet
85
Applety
Tag APPLET Pro vložení appletu do HTML kódu stránky je třeba znát parametry tagu <APPLET>. Tučně jsou označeny povinné části, nepovinné parametry jsou v hranatých závorkách, část
se může v tagu opakovat s různými hodnotami. <APPLET [CODEBASE = URL Path] CODE = filename [ARCHIVE = archiveName] WIDTH = pixelWidth HEIGHT = pixelHeight [ALT = text ] [NAME = name] [ALIGN = alignment] [VSPACE = verticalSpace] [ HSPACE= horizintalSpace] > [
…]
První jednoduchý applet Nyní si ukážeme, jak napsat jednoduchý applet, který zobrazí text. Jak už bylo řečeno, v appletu jsou automaticky spouštěny metody podle akce uživatele. Pokud chceme zobrazit text, zařadíme tuto akci do metody paint, která má jako parametr instanci třídy Graphics (tento parametr bude dosazen systémem při spuštění). Třída Graphics obsahuje množství metod pro kreslení. Jednou z těchto metod je i metoda drawString (String s, int x, int y), kde x a y jsou souřadnice umístění levého dolního okraje textu. Souřadnicový systém třídy Graphics začíná v levém horním rohu appletu. x
y
Počáteční souřadnice appletu 0,0
AHOJ SVETE Souřadnice umístění textu
import java.applet.*; import java.awt.*; public class PrvniApplet extends Applet { public void paint (Graphics g) { g.drawString("Ahoj svete",20,20); } }
86
Applety Celý HTML kód stránky obsahující applet
<TITLE>stranka s appletem <APPLET CODE="PrvniApplet.class" WIDTH="200" HEIGHT="50">
Applet s parametrem Na dalším appletu si kromě použití parametru ukážeme i nastavování barev a fontů. Vytvoříme applet, který na žlutém pozadí zobrazí modrý text APPLET S PARAMETREM písmem Serif (viz minulá kapitola), tučně, s velikostí písma 20. Pod textem bude červený vyplněný čtverec s délkou strany nastavenou přes parametr appletu. Parametr je do appletu načten jako řetězec, je ho tedy nutné převést na číslo. Tento převod je třeba udělat pouze jednou na začátku, proto ho umístíme do metody init. Abychom mohli získanou hodnotu použít v metodě paint, musíme si zavést proměnou appletu strana. I font je vhodné definovat pro celý applet. V metodě init také nastavíme barvu pozadí appletu. Do metody paint zařadíme nastavení fontu a barvy textu, jeho zobrazení a nastavení barvy a vykreslení čtverce. import java.applet.*; import java.awt.*; public class AppletSParametrem extends Applet { int strana; Font f1 = new Font ("Serif",Font.BOLD,20); public void init() { String str = getParameter("rozmer"); strana = Integer.parseInt(str); setBackground(Color.yellow); } public void paint (Graphics g) { g.setFont(f1); g.setColor(Color.blue); g.drawString("APPLET S PARAMETREM",30,30); g.setColor(Color.red); g.fillRect(30,40,strana,strana); } }
87
Applety HTML kód stránky s naším appletem pak může vypadat takto: <TITLE>stranka s appletem <APPLET CODE="AppletSParametrem.class" WIDTH="500" HEIGHT="500">
88
Přílohy
10. Přílohy Příloha 1 – Java SDK od firmy Sun Firma SUN v současné době nabízí na http://java.sun.com/ tři verze vývojového prostředí (SDK – Software Development Kit) pro Javu: -
Java 2 Standard Edition – určeno pro vývoj "běžných" aplikací v Javě, která je nyní k dispozici ve verzi 1.3.1 pro tří prostředí: 32-bitová Windows, Sun Solaris a Linux, dokumentace je k dispozici v HTML formátu,
-
Java 2 Enterprise Edition – určeno pro vývoj celopodnikových aplikací pro různé platformy,
-
Java 2 Micro Edition – určeno pro vývoj aplikací v Javě pro "malá" zařízení (handheldy, ...),
Instalace vývojového prostředí pro Javu je v prostředí Windows jednoduché – z WWW stránky se stáhne instalační soubor (program.exe o velikosti přibližně 30MB) a ten se spustí. Je vhodné si nastavit do proměnné PATH adresář se spustitelnými programy (obvykle C:\JDK1.3\BIN), aby jejich spouštění bylo jednodušší. Firma SUN dále nabízí RunTime Environment,což je prostředí pro spouštění již přeložených aplikací v Javě, součástí je i tzv. Java Plug-In – podpora pro spouštění apletů v aktuálních verzích WWW prohlížečů, RunTime prostředí je k dispozici ve dvou verzích – US English verzi a v mezinárodní (Internationalized) verzi. Na stránkách http://java.sun.com/ jsou dále k dispozici různé další knihovny a doplňky k jazyku Java (např. knihovna pro šifrování, knihovna pro zpracování pošty, prostředí pro servlety a JSP).
Vytvoření a spuštění programu, V příkazu java se udává jméno třídy, ve které spustí metodu třídy main. Příslušný soubor třídy (soubor .class) se vyhledává ve standardní knihovně javy (rt.jar) a v aktuálním adresáři. Pokud je definována proměnná CLASSPATH, hledá se příslušná třída i v adresářích a archivech uvedených v této proměnné. Pokud je v definici třídy uveden balík (package), musí se jméno třídy uvést včetně celého jména balíku15. V tomto případě musí být příslušný soubor .class v odpovídajícím podadresáři. Vysvětlíme si to na následujícím příkladě: Nechť je definována následující struktura adresářů C:\java \cz \vse \it_112 a v posledním adresáři nechť je přeložený program pole.class, který má definován package cz.vse.it_112; 15
V tomto pravidle je jedna výjimka – jméno balíku se nemusí uvádět v případě třídy java.lang. 89
Přílohy poté lze spustit tento program jedním z následujících způsobů: java cz.vse.it_112.pole tato varianta spuštění je úspěšná při splnění aspoň jednoho z následujících předpokladů: proměnná prostředí CLASSPATH se odkazuje na adresář C:\JAVA příkaz se zadá v adresáři C:\JAVA, java cz/vse/it_112/pole ve jménu třídy lze nahradit tečky lomítky java –cp c:\java cz.vse.it_112.pole cesta ke třídě se uvede na příkazové řádce. Následující způsoby spuštění jsou chybné: java cz\vse\it_112\pole
- obrácená lomítka
java pole
- není celý název třídy
java cz.vse.it_112.Pole
- velké písmeno místo malého
v těchto případech vypadá chybové hlášení následovně: Exception in thread "main" java.lang.NoClassDefFoundError: pole (wrong name: cz/vse/it_112/pole) at java.lang.ClassLoader.defineClass0(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:403) at java.security.SecureClassLoader.defineClass (SecureClassLoader.java:101) at java.net.URLClassLoader.defineClass (URLClassLoader.java:248) at java.net.URLClassLoader.access$1(URLClassLoader.java:216) at java.net.URLClassLoader$1.run(URLClassLoader.java:197) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:191) at java.lang.ClassLoader.loadClass(ClassLoader.java:280) at sun.misc.Launcher$AppClassLoader.loadClass (Launcher.java:275) at java.lang.ClassLoader.loadClass(ClassLoader.java:237) Pokud se soubor s příslušným jménem nenajde, vypadá chybové hlášení následovně (následující chyba vznikla po zadání příkazu java cz/vse/it_112/pole1, přičemž neexistuje soubor pole1.class): Exception in thread "main" java.lang.NoClassDefFoundError: cz/vse/it_112/pole1 V případě mnohých aplikací v Javě je vhodné si pro jejich spouštění vytvořit dávkový soubor. Např. pro spouštění programu Jindent může dávkový soubor jindent.bat obsahovat následující řádek: java –cp c:\javautil\jindent.jar Jindent –cr –w –f %1
Programy obsažené v Java SDK od firmy SUN javac - příkazem javac se spouští překladač, který zkontroluje syntaxi vytvořeného programu a v případě, že je bez syntaktických chyb, vytvoří bytecode tj. soubory s koncovkou class (z jednoho souboru s koncovkou java vznikne tolik souborů s koncovkou class kolik obsahuje tříd včetně vnitřních). 90
Přílohy java – příkaz ke spouštění aplikací, má dvě základní syntaxe: java [volby] jméno_třídy [parametry] java [volby] –jar archiv.jar [parametry] V první variantě je parametrem jméno třídy – tato třída musí obsahovat metodu public static void main (String args[]) která se spustí a které se předají případné parametry z příkazové řádky. V druhé variantě se spouští aplikace uložená v archivu jar – podrobněji je to popsáno v příloze číslo 4. Z voleb je nejdůležitější specifikace adresářů a jar souborů, ve kterých se mají vyhledávat jednotlivé třídy (soubory class) – to se provádí pomocí volby –classpath (či zkráceně –cp). Adresáře a archivy lze též specifikovat pomocí environment proměnné CLASSPATH. Následuje ukázka dávkového souboru pro spouštění programu bluej, který hledá jednotlivé třídy ve 4 souborech jar (a v archivu rt.jar, který obsahuje základní třídy a který se prohledává automaticky): set CLASSPATH=c:\bluej\lib\bluej.jar;c:\bluej\lib\editor.jar set CLASSPATH=%CLASSPATH%;c:\bluej\lib\antlr.jar; set CLASSPATH=%CLASSPATH%;C:\jdk1.3\lib\tools.jar C:\jdk1.3\bin\java bluej.Main %1 %2 %3 %4 %5 %6 %7 %8 %9 javaw – stejný příkaz jako java s tím rozdílem, že se nevytváří konzolový vstup/výstup. Obvykle se používá pro spouštění grafických aplikací. appletviewer – program pro testování apletů, javadoc – program slouží pro dokumentování vytvořených tříd z komentářů vložených do zdrojového textu programu, ukázka použití je uvedena v příloze č. 3. jar – tento program slouží pro vytváření archivů .jar, které obsahují větší množství tříd a jsou zkompresovány. Použití archivů jar umožňuje rychlejší provádění programů (méně souborů, menší objem) a jednodušší distribuci aplikací (jeden soubor). Podrobnější popis programu jar je v příloze 4. javap - The Java Class File Disassembler – umožňuje metody a proměnné přeloženého programu (souboru .class) a s parametrem –c převést tento soubor do čitelné formy. Od jiných výrobců jsou k dispozici nástroje pro převod souborů .class do zdrojového textu. Ve většině vývojových prostředí i jako samostatné produkty jsou k dispozici nástroje, které mají za úkol zabránit zpětnému generování zdrojového kódu – pro tuto činnost se v Javě používá pojem obfuscate (zatemnit, poplést). jdb – Java Debugger – je přes příkazovou řádku ovládaný jednoduchý nástroj pro krokování programů v Javě, který umožňuje definovat zarážky (breakpoints) a vypisovat obsahy proměnných. Grafická vývojová prostředí obvykle obsahují komfortnější možnosti pro krokování programu. keytool, jarsigner a policytool slouží k zabezpečení vytvořených aplikací v Javě – program keytool generuje dvojice klíčů a vytváří self-signed certifikáty dle X.509 v1, dále spravuje klíče uložené v zásobníku klíčů (keystore). Klíče a certifikáty se používají k digitálnímu podepisování vytvořených aplikací a apletů. Jarsigner digitálně podepisuje aplikace v souboru JAR a umožňuje v souborech JAR zkontrolovat digitální podpisy. K tomu využívá uživatelský zásobník klíčů.
91
Přílohy rmic, rmid, rmiregistry a serialver – tyto programy se používají při tvorbě a ladění vzdálených procedur vytvářených dle specifikace Remote method invocation (RMI), javah – generování hlavičkových souborů objektů potřebných k tomu, aby se k nim dalo přistupovat z jazyka C, pokud se při volání z jazyka C postupuje dle specifikace JNI (Java Native Interface) nejsou hlavičkové soubory obvykle potřeba.
92
Přílohy
Příloha 2 – Popis originální dokumentace Javy od firmy SUN
Jméno třídy Hierarchie třídy Implementovaná rozhraní
Úplná hlavička třídy
Dostupné proměnné třídy. Typy a modifikátory proměnných. Přehled konstruktorů třídy. Přehled veřejných (public) a protected metod třídy.
V prvním sloupci je uveden typ návratové hodnoty a případné modifikátory (static, protected, final, ...)
Ve druhém sloupci jsou uvedeny názvy metod, parametry a stručný popis. Všimněte si, že přetížené metody (např. compareTo) jsou uvedeny vícekrát.
93
Přílohy
Přehled metod zdědených z předků. Následuje popis jednotlivých proměnných, konstruktorů a metod.
Ukázka popisu jedné metody na příkladu metody valueOf.
Úplné hlavičky metody.
Popis metody.
Popis parametrů metody. Popis návratové hodnoty.
94
Seznam výjimek, které v metodě mohou vzniknout.
Přílohy
Příloha 3 - Konvence pro psaní programů v Javě V této příloze jsou popsané konvence pro psaní programů v Javě založené na standardech firmy SUN. Jejich cílem je usnadnit čtení programů vytvořených jinými osobami a omezení některých chyb, které mohou vznikat z nepozornosti.
Jména souborů Pro přípony souborů existují následující standardy: Java source
.java
Java bytecode
.class
Java archive
.jar
Součástí každého adresáře by měl být soubor README či index.html, ve kterém je popsán obsah adresáře.
Struktura zdrojového souboru Javy Soubory by neměly mít více než 2000 řádek. Každý zdrojový soubor obsahuje jednu veřejnou třídu/rozhraní. Veřejná třída by měla předcházet privátní třídy a rozhraní v tomto souboru. Zdrojový soubor by se měl skládat z následujících části: -
úvodní komentáře, které by měly obsahovat název souboru, historii změn a případné autorské údaje, viz příklad:
/* * File: Kniha.java * * 21 jan 2001 xabcd01 * 26 jan 2001 xabcd01 */
prvni verze doplnena metoda vlozISBN
-
příkazy package a import
-
vlastní třídy a rozhraní včetně popisu jednotlivých tříd/rozhraní a metod. Následující tabulka obsahuje přehled jednotlivých částí třídy/rozhraní v doporučeném pořadí: Část deklarace třídy/rozhraní dokumentace k třídě/rozhraní (/** .... */) příkaz class nebo interface komentáře k implementaci třídy/rozhraní (pokud jsou potřeba) proměnné třídy (static) proměnné instance konstruktory metody
Poznámky popisy a komentáře, které převezme Javadoc komentáře k implementaci celé třídy, u kterých není vhodné, aby byly v dokumentaci generované pomocí Javadoc v pořadí public, protected, standardní (tj. bez modifikátorů přístupu) a nakonec private ve stejném pořadí, jako proměnné třídy metody by měly být seřazeny dle funkční příbuznosti, tak aby čtení bylo snazší
95
Přílohy Zarovnávání, odsazování Odsazovat by se mělo po 4 mezerách. Jednotlivé řádky by neměly být delší než 80 znaků, neboť poté jsou obtížně zobrazované na mnoha terminálech či v některých nástrojích. Řádky je vhodné rozdělovat za čárkou či před operátorem.
Komentáře Rozlišují se dva typy komentářů: -
dokumentační, které se zahrnují do dokumentace vytvářené programem Javadoc (/** .... */),
-
implementační, které se podobají komentářům z C++ (/* ....*/ a //)
Dokumentační komentáře musí být uvedeny před každou deklarací třídy, metody, rozhraní či proměnné, které mají modifikátor přístupu public či protected. Začínají znaky /** a končí znaky */. Text může obsahovat formátovací znaky HTML a speciální příznaky pro Javadoc: @author
jméno-autora třídy
@version
číselné označení verze třídy
@see
odkaz na jinou třídu/metodu či na nějaké URL
@param
jméno_parametru
@return
popis návratové hodnoty z metody
@exception
jméno_výjimky
popis parametru popis výjimky, která může nastat v metodě
Následuje příklad dokumentačních komentářů: /** * Tato trida slouzi ke generovani vystupu s ruznymi zpusoby * kodovani ceskych znaku * *@author xabcd01 *@created 27. may 2001 */ public class Unicode { /** *obsahuje seznam ceskych znaku v kodovani Unicode v notaci Javy */ public static final String retezec = "\u00e1\u00c1\u00e9 ..."; /** * metoda main dle prvniho parametru prikazove radky vytiskne .* retezec na standardni vystup v pozadovanem kodovani * *@param ARGS prvni parametr prikazove radky obsahuje retezec * specifikujici pozadovane kodovani .*@see Supported Encoding v dokumentaci JDK */ public static void main(String ARGS[]) { ... Příkaz javadoc Unicode.html vygeneruje dokumentaci ve formátu html.
96
Přílohy Implementační komentáře slouží ke komentování implementace třídy a měly by se používat v případě, kdy slouží ke lepšímu čtení či porozumění kódu. Neměly by duplikovat informace, které lze snadno vyčíst z vlastního kódu. Potřeba psát implementační komentáře je někdy příznakem nízké kvality návrhu kódu – v tomto případě je vhodnější přepsat kód. Následují příklady implementačních komentářů: /* * komentář, který je na více * řádcích */ /* jednořádkový komentář */ příkaz
// komentář, který končí na konci řádku
Deklarace Na každém řádku by měla být uvedena deklarace pouze jedné proměnné: int velikost;
// velikost souboru
Součásti deklarace by měla být i inicializace s výjimkou případů, kdy počáteční hodnota závisí na nějaké operaci. Deklarace by měly být na začátku bloku (na začátku metody, na začátku třídy, ...) a ne až před prvním použitím. Pro toto pravidlo existuje jedna výjimka – řídící proměnná cyklu u příkazu for: for (int i = 0; i < maxLoops; i++) { ... }
Příkazy Na každém řádku by měl být pokud možno jen jeden příkaz. Následují doporučené způsoby formátování jednotlivých jazykových konstrukcí: if (podmínka) { příkazy } else { příkazy } while (podmínka) { příkazy } try { příkazy; } catch (ExceptionClass e) { příkazy; } finally { příkazy; }
97
Přílohy
Mezery Prázdné řádky zvyšují čitelnost kódu, pokud oddělují logické celky. Okolo operátorů (+ - * = ...) by měly být na obou stranách mezery. Mezery by měly být i před a za závorkou.
Konvence pro pojmenovávání Typ identiPravidlo pro pojmenování fikátoru balík (package) jméno balíku by mělo obsahovat na začátku jméno domény organizace v obráceném pořadí (cz.vse, com.sun), další strukturování záleží na vnitřních konvencích organizace, jméno by mělo obsahovat pouze malá písmena, třída (class) jméno třídy by mělo být podstatné jméno (více podstatných a přídavných jmen) začínající velkým písmenem rozhraní obdobně jako u třídy (interface) metoda jméno metody by mělo obsahovat sloveso, mělo by začínat malým písmenem, další slova v názvu by měla začínat velkým písmenem proměnná jméno proměnné by mělo začínat malým písmenem, pokud obsahuje více slov, tak další by měla začínat velkým písmenem, jméno proměnné by nemělo obsahovat znaky $ a _, jméno proměnné by mělo vyjadřovat obsah, neměly by se používat jednoznaková jména s výjimkou řídících proměnných cyklů typu integer (proměnné i, j, k, l, m, n) konstanta jména konstant by měla být složena z velkých písmen, pro oddělení slov je použito podtržítko _
98
Příklady com.sun.eng org.acm.sequin.pretty cz.vse.xabcd01.ukol2 org.gjt.mm.mysql class Kniha; class Casopis; class VstupniSchranka; interface Publikace; interface Schranka; vratMax(); ulozDelku(); nactiVetu(); Date datumNarozeni; int pocetVet; float studijniPrumer; int i;
static final int MIN_WIDTH = 4; static final int GET_THE_CPU = 1;
Přílohy
Příloha 4 – Jar – vytváření a používání archivů Formát souborů JAR (Java Archive), které se používají pro seskupování více souborů (většinou soubory .class, mohou být i další) do jednoho zkompresovaného, je odvozen z formátu souborů ZIP. Soubory JAR jsou přenositelné mezi jednotlivými platformami a mohou obsahovat další doplňující informace o vložených souborech – používá k tomu tzv. „manifest“ soubor (METAINF\MANIFEST.MF) a případně soubory s digitálními podpisy (META-INF\*.SF). Soubory JAR se vytvářejí pomocí programu jar, který je součástí Java SDK. Program jar má parametry příkazové řádky odvozené od archivačního program tar, který se používá v unixových operačních systémech, a vypadá následovně: jar [volby] archiv vstupní_soubor[y] Volby jsou následující: C U T X
x soubor F
V
0 m manifest M
bude se vytvářet archiv (create) existující archiv se bude aktualizovat (přidávat či měnit soubory) vypíše se obsah existujícího archivu (table of contents) soubory z archivu se zapíší do aktuálního adresáře a případně do podadresářů (extract) z archivu se vypíše pouze zadaný soubor je zadán vstupní/výstupní archiv (jeho jméno je nutné uvádět i s koncovkou .jar), pokud není uveden tento parametr, použije se standardní vstup (s parametrem x či t) nebo standardní výstup (při vytváření) na obrazovce zobrazuje podrobněji svoji činnost (verbose) soubory se pouze vkládají, nekompresují uživatelem vytvořený soubor manifest je uveden na příkazové řádce nebude se vytvářet soubor MANIFEST.MF
Příklady použití: vytvoření archivu: jar cvf mujArchiv.jar *.class jar cvf it_112.jar cz\vse\it_112\*.class aktualizace archivu jar uvf it_112.jar cz\vse\it_112\*.class výpis obsahu archivu jar tvf it_112.jar extrahování souboru z archivu do aktuálního adresáře a podadresářů jar xvf it_112.jar vytvoření archivu včetně vlastního manifest souboru jar cvfm mujManifest.mf mujArchiv.jar *.class
99
Přílohy Do jar archivu lze vkládat (a z něho používat) i obrázky a zvukové soubory. Soubor jar lze specifikovat v proměnné CLASSPATH pro vyhledávání tříd (souborů .class): set CLASSPATH=”mujArchiv.jar;it_112.jar;.” - java bude vyhledávat soubory .class i v archivech mujArchiv.jar, it_112.jar a v aktuálním adresáři (a samozřejmě ve standardním archivu jre\lib\rt.jar). Následují dva příklady použití souborů jar v appletu:
V pomocném souboru MANIFEST.MF v archivu jar mohou být uloženy informace o verzích tříd, digitální podpisy tříd a některé další. Zde si popíšeme, jak vytvořit spustitelný archiv. Pokud máte aplikaci zabalenou v souboru JAR, potřebujete v souboru Manifest.mf označit třídu, která se má spouštět (tj. třídu, ze které se má spustit metoda public static void main (String[] args)). Toho se docílí řádkem: Main-Class: třída v Manifest souboru, který se vloží programem jar do archivu. Vlastní spuštění aplikace poté proběhne následujícím příkazem java –jar archiv.jar
100
Přílohy
Příloha 5 - Kódování českých znaků v Javě V souvislosti s Javou se můžete setkat s následujícími způsoby kódování českých znaků: -
Unicode – pro každý znak se používá 16 bitů, toto kódování používá Java vnitřně, pokud chcete zadat znak v tomto kódování v řetězci, je nutné mu předřadit znaky \u,
-
UTF-8 – jedná se v podstatě o jiný způsob zápisu znaků Unicode (obdobně jako zápis \u....), přičemž se používá proměnlivá délka pro jednotlivé znaky (přibližně lze říct, že pro znaky anglické abecedy se používá jeden byte, pro znaky dalších hláskových abeced dva byte, pro znaky obrázkových abeced se používají tři byte). Existuje jednoznačný algoritmus pro převod mezi UTF8 a Unicode. Kódování UTF-8 se výrazně prosazuje v protokolech na Internetu i v Unixu.
-
CP1250 – kódování znaků středo a východoevropských národů nepoužívajících azbuku v MS Windows,
-
ISO8859_2 – mezinárodní standard kódování znaků středo a východoevropských národů nepoužívajících azbuku, používá se např. v Unixu a v některých protokolech na Internetu,
-
CP852 – kódování znaků středo a východoevropských národů nepoužívajících azbuku v DOSu (standard IBM používaný i firmou Microsoft),
-
MacCentralEurope – kódování znaků středo a východoevropských národů nepoužívajících azbuku na MacIntoshi,
znak Unicode hex. zápis á 00e1 Á 00c1 č 010d Č 010c ď 010f Ď 010e é 00e9 É 00c9 ě 011b Ě 011a í 00ed Í 00cd ň 0148 Ň 0147 ó 00f3 Ó 00d3 ř 0159 Ř 0158 š 0161 Š 0160 ť 0165 Ť 0164
Unicode zápis v Javě \u00e1 \u00c1 \u010d \u010c \u010f \u010e \u00e9 \u00c9 \u011b \u011a \u00ed \u00cd \u0148 \u0147 \u00f3 \u00d3 \u0159 \u0158 \u0161 \u0160 \u0165 \u0164
UTF-8 hex. zápis c3a1 c381 c48d c48c c48f c48e c3a9 c389 c49b c49a c3ad c38d c588 c587 c3b3 c393 c599 c598 c5a1 c5a0 c5a5 c5a4
CP1250 hex. zápis e1 c1 e8 c8 ef cf e9 c9 ec cc ed cd f2 d2 f3 d3 f8 d8 9a 8a 9d 8d
ISO8859_2 hex. zápis e1 c1 e8 c8 ef cf e9 c9 ec cc ed cd f2 d2 f3 d3 f8 d8 b9 a9 bb ab
CP852 hex. zápis a0 b5 9f ac d4 d2 82 90 d8 b7 a1 d6 e5 d5 a2 e0 fd fc e7 e6 9c 9b
MacCentralEurope 87 E7 8B 89 93 91 8E 83 9E 9D 92 EA CB C5 97 EE DE DB E4 E1 E9 E8
101
Přílohy ú Ú ů Ů ý Ý ž Ž a A
00fa 00da 016f 016e 00fd 00dd 017e 017d 0061 0041
\u00fa \u00da \u016f \u016e \u00fd \u00dd \u017e \u017d a A
c3ba c39a c5af c5ae c3bd c39d c5be c5bd 61 41
fa da f9 d9 fd dd 9e 8e 61 41
fa da f9 d9 fd dd be ae 61 41
a3 e9 85 de ec ed a7 a6 61 41
9C F2 F3 F1 F9 F8 EC EB 61 41
Pokud ve zdrojovém textu programu jsou v řetězci použity národní znaky, překladač javac odhaduje jejich kódování dle svého prostředí – tj. např. v českých Windows (i v DOS okně) kódování CP1250, v Unixu v závislosti na nastavení locales (ISO8859_2 nebo často ISO8859_1). Proto je z důvodů přenositelnosti vhodnější psát národní znaky v Unicodu (tj. např. á ve tvaru \u00e1). Při běhu javovských programů se též provádí konverze znaků – vstupu do kódování Unicode a výstupu z kódování Unicode. Z kterého a do kterého kódu se převádí záleží opět na prostředí, ve kterém se program spouští. Aktuálně používanou kódovou stránku lze vypsat pomocí System.getProperty("file.encoding"). Třídy InputStream, OutputStream a jejich potomci mají problémy při zpracování textových vstupů/výstupů s ohledem na správné konverze kódů a z tohoto důvodů v Javě 1.1 přibyly třídy Reader a Writer. Programátor má možnost určit znakovou sadu v konstruktorech tříd InputStreamReader a OutputStreamWriter. V instancích těchto tříd lze též zjistit aktuální znakovou sadu metodou getEncoding(). Pro třídění řetězců s národními znaky dle českých pravidel se používá třída Collator. Další podrobnosti o kódování a lokalizaci programu lze nalézt v dokumentaci JDK.
102
Přílohy
Příloha 6 - Seznam termínů a zkratek Termín Abstraktní metoda -
Definice pouze deklarace metody v abstraktní třídě, v potomcích této třídy musí být metoda naimplementována
Abstraktní třída
třída sloužící jako předek několika tříd jejichž společné vlastnosti představuje, obsahuje abstraktní metody, nelze vytvářet instance této třídy pomocí vlastního konstruktoru
Anonymní vnitřní třída
nepojmenovaná vnitřní třída, obvykle definovaná na místě parametru metody vnější třídy
Applet
programy běžící v rámci WWW stránky na stanici klienta v prohlížeči
Balík (package)
pojmenovaná knihovna tříd a rozhraní
Datový typ proměnné
označuje typ dat uložených v proměnné např. číslo, text, instanci konkrétní třídy
Dědičnost
vztah mezi třídami, umožňuje k základní třídě (předkovi) přidat další vlastnosti a vytvořit tak odvozenou třídu (potomka)
Dynamické datové struktury (kontejnery)
třídy, jejichž instance slouží k uložení odkazů na předem neznámý počet instancí jiných tříd
Garbage collector (čistič paměti)
speciální proces, který se stará o uvolńování nepotřebných objektů z paměti
GUI (Graphic User Interface)
grafické uživatelské rozhraní
Hodnota proměnné
vlastní obsah proměnné
HTML (Hypertext Markup Language)
jazyk pro vytváření hypertextových dokumentů
Identifikátor proměnné
pojmenování proměnné
Iterátor (Iterator)
objekt, jehož úkolem je zpřístupnit prvky kontejneru
Java
objektově orientovaný programovací jazyk
Java archive (JAR)
archiv přeložených tříd a doplňujících souborů, používá se při distribuci aplikací
Java bytecode
mezikód vytvořený překladačem, který je interpretován JVM
Javadoc
program sloužící pro vytváření programátorské dokumentace tříd ve formátu HTML
JDK (Java Development Kit)
základní prostředí pro vytváření a spouštění programů v Javě
JVM (Java Virtual Machine)
platforma pro spuštění aplikací v Javě (zajišťuje vazbu na HW a OS a interpretuje bytecode)
103
Přílohy Termín
Definice
Kolekce (Collection)
lineární dynamická datová struktura
Konstanta
proměnná jejíž hodnota se nemění
Konstruktor
speciální metoda pro vytváření instancí tříd
Mapa (Map) – též asociativní pole nebo
kontejner, který umožňuje ukládat dvojice klíč a hodnotu, klíč
slovník (Dictionary)
musí být jednoznačný
Metoda
funkční složka objektu, pomocí metod popisujeme činnost objektů
Metoda instance
metoda, která se provádí pro konkrétní instanci třídy, bez existence instance není možno ji spustit
Metoda třídy
metoda, kterou je možno spustit bez existence instance,spouští se se jménem třídy
Množina (Set)
lineární dynamická datová struktura, ve které má každý prvek jedinečnou hodnotu
Modifikátor třídy
klíčové slovo uvedené před slovem class určující další vlastnosti třídy
Objekt
představuje souhrn dat a činností, které od něj můžeme žádat. Každý objekt má určité rozhraní, prostřednictvím kterého komunikuje s jinými objekty.
Polymorfismus
možnost využívat v programovém kódu stejnou syntaktickou podobu pro metody s různou vnitřní reprezentací
Proměnná
proměnnou určují tři části: identifikátor, datový typ a vlastní hodnota proměnné,
Přetečení proměnné
týká se numerických proměnných a označuje stav, kdy při aritmetické operaci dojde k překročení rozsahu této proměnné
Přetěžování metod (overloading)
možnost vytvořit v jedné třídě více metod stejného jména, které se liší počtem nebo pořadím nebo typem parametrů
Přetypování
změna/upřesnění datového typu proměnné
Rozhraní (interface)
definuje hlavičky metod, které musí každá třída implementující toto rozhraní naimplementovat
Servlet
programy běžící na WWW serveru
Seznam (List)
lineární dynamická datová struktura, ve které se hodnoty jednotlivých prvků mohou opakovat
Třída (class)
je obecný popis vlastností a činností objektů
UML (Unified Modelling Language)
standard pro zápis/zobrazení objektových modelů
104
Přílohy Termín Unicode
Definice rozšířená znaková sada využívající 16 bitů pro uložení jednotlivých znaků
URL (Uniform Resource Location)
identifikátor označující umístění dokumentu v rámci Internetu
UTF8
pravděpodobně nejrozšířenější způsob zápisu/přepisu/přenosu znaků v Unicode pomocí "běžných" znaků, standard v rámci Internetu
Vnitřní třída
třída definovaná uvnitř jiné třídy
Vstupní a výstupní proudy (Input/Output
zobecnění vstupů a výstupů
streams) Výjimka (exception)
chybový stav, který narušuje běžný postup zpracování metody
Zapouzdření
vlastnost tříd a instancí, možnost skrýt implementaci třídy a poskytnout uživateli rozhraní potřebné pro použití třídy
105
Přílohy
Literatura a odkazy Literatura Chan M.C., Griffith S. W., Iasi A. F.: 1001 tipů Java. Unis Publishing 1997. ISBN 80-86097-11-0. Eckel B.: Thinking in Java. Prentice Hall, 1998. ISBN 0-13-659723-8. Eckel B.: Thinking in Java 2nd Edition. Prentice Hall, 2000. ISBN 0-13-027363-5. Eckel B.: Myslíme v jazyku Java, knihovna programátora. První část překladu anglického originálu. Grada Publishing, 2001. ISBN 80-247-9010-6. Eckel B.: Myslíme v jazyku Java, knihovna zkušeného programátora. Druhá část překladu anglického originálu. Grada Publishing, 2001. ISBN 80-247-0027-1. Grand M.: Java - Referenční příručka jazyka. Druhé vydání. Computer Press 1998. ISBN 80-7226071-5. Herout P.: Učebnice jazyka Java. Kopp 2000. ISBN 80-7232-115-3. Rowe G. W.: An Introduction to Data Structures and Algorithms with Java. Prentice Hall 1998. ISBN 0-13-857749-8. Virius M., Štrupl D.: JBuilder verze 3, podrobný průvodce. Grada Publishing 1999. ISBN 80-7169890-3.
Informace o Javě na Internetu java.sun.com/j2se/1.3/docs/ on-line dostupná dokumentace JDK od firmy SUN, java.sun.com/docs/books/tutorial/index.html tutoriál k výuce Javy od firmy SUN, je provázán s on-line dokumentací, www.bruceeckel.com zde jsou volně ke stažení knihy B. Eckela týkající se jazyků Java a C++, java.sun.com informace o Javě od firmy SUN, www.javaworld.com na tomto serveru lze najít množství informací pro vývojáře v Javě, www.javalobby.com zpravodajský server specializovaný na oblast Javy, www.jars.com server s velkým množstvím aplikací a knihoven objektů v Javě, www.ibm.com/java www stránky firmy IBM zaměřené na Javu, kde jsou k dispozici různé produkty pro Javu, výukové kursy zaměřené na Javu i mnoho informací pro vývojáře v Javě,
106
Přílohy Vývojová prostředí a nástroje pro Javu Java SDK (java.sun.com ….) Firma SUN vytváří základní vývojová prostředí pro jazyk Java pro operační systémy Solaris, Linux a Windows 9x/NT/2000. Další firmy vytvářejí vývojová prostředí pro další operační systémy (např. IBM). Nad těmito základními vývojovými prostředími vznikají grafická vývojová prostředí dalších firem, která umožňují pohodlnější vývoj aplikací. BlueJ (www.bluej.org) Grafická nadstavba nad Java SDK, určená pro výuku Javy (se zaměřením na definici objektů a vazeb mezi objekty) napsaná na univerzitě Monash v Austrálii. V poslední verzi umožňuje dokumentovat vazby mezi objekty pomocí UML. JBuilder (www.borland.com) Firma Inprise/Borland nyní uvolnila základní verzi svého produktu k bezplatnému používání. Verze Professional a Enterprise jsou rozšířené o další vývojové nástroje pro vytváření rozsáhlých aplikací. NetBeans (www.netbeans.org) Vývojové prostředí vytvořené českou firmou NetBeans, nyní rozvíjené jako OpenSource. Forte (forte.sun.com) Vývojové prostředí založené na NetBeans vyvíjené a distribuované pod hlavičkou firmy SUN. Základní verze je distribuována zdarma. IBM VisualJava (www.ibm.com/java) Kvalitní vývojové prostředí od firmy IBM, jejíž základní verze (s omezením na 800 objektů) je volně ke stažení. Jikes (www.ibm.com/developerworks/opensource) Překladač (náhrada programu javac, k používání potřebuje nainstalovaný Java SDK) od firmy IBM, který se šíří pod GNU licencí. Tento překladač je rychlejší a méně náročný na výkonnost počítače než javac z Java SDK. Program Jikes používá stejné parametry na příkazové řádce jako javac, z rozšíření doporučuji prostudovat parametr ++ (překlad v inkrementálním režimu, který ještě více zrychluje překlad při vývoji aplikace) a +P, kdy překladač kontroluje některé další chyby. Výstup je často menší, než výstup překladače javac – program jikes nezahrnuje do výstupu kód, který se nepoužívá. Jlint (www.ispras.ru/~knizhnik) V distribuci se nacházejí dva programy. Program antic, který hledá problematické konstrukce ve zdrojovém souboru (např. chybějící závorky v matematických výrazech, chyby v konstrukci case …). Druhý program jlint hledá v přeloženém programu (v bytecodu) podezřelé konstrukce, které obvykle znamenají logickou chybu. Program jlint pro vyhledání chyb potřebuje překlad s parametrem –g (začlenění tabulky lokálních proměnných do bytecodu). Doporučuji programy antic a jlint nainstalovat a používat. 107
Přílohy Jopt (www-i2.informatik.rwth-aachen.de/~markusj/jopt) Program Jopt (Java Class File Optimizer) analyzuje všechny metody v programu a snaží se zrušit ty části, které nejsou potřeba. V průměru se sníží velikost programu (souboru jar) o 30%. Současně provádí drobné optimalizace bytecodu. Realj (www.realj.org) Jednoduché vývojové prostředí pro Javu napsané v jazyku C, které lze zdarma používat pro výuku a je vhodné pro méně výkonné počítače. JCreator (www.jcreator.com) Další vývojové prostředí pro Javu napsané v jazyku C s nízkými nároky na technické vybavení, které lze zdarma používat.
108