Programovßnφ v jazyce Java: 7. dφl
David ètrupl
Zp∙sobenφ chyby
V jazyce Java mßme mo₧nost chyby (v²jimky) nejen zpracovßvat, ale takΘ je m∙₧eme zp∙sobovat. K vyvolßnφ chyby slou₧φ p°φkaz throw. Jako parametr musφ b²t objekt -- potomek t°φdy Throwable, kter² bude reprezentovat nastalou chybu.
CelΘ zp∙sobenφ chyby m∙₧e vypadat nap°. takto:
...
throw new FileNotFoundException();
P°φkaz throw musφ b²t bu∩ uveden ve strß₧enΘm bloku, nebo musφme oznaΦit metodu, kterß jej obsahuje. Metodu zp∙sobujφcφ v²jimku oznaΦφme klφΦov²m slovem throws nßsledovan²m druhem v²jimky:
void mojeMetoda() throws FileNotFoundException {
...
throw new FileNotFoundException();
}
Z deklarace (a tedy takΘ z dokumentace) k jednotliv²m balφk∙m (knihovnßm) je potom v₧dy jasn∞ patrnΘ, kterΘ metody mohou zp∙sobovat v²jimky.
Druhy chyb
Jak ji₧ bylo uvedeno v²Üe, vÜechny zp∙sobitelnΘ chyby jsou potomky t°φdy Throwable. Tyto t°φdy lze dßle rozd∞lit na t°i velkΘ skupiny -- potomky t°φd: Error, Exception a RuntimeException.
T°φda Error a jejφ potomci reprezentujφ chyby JVM. P°φkladem takovΘ chyby m∙₧e b²t OutOfMemoryError. Tyto chyby by v u₧ivatelsk²ch programech asi ani nem∞ly b²t zpracovßvßny, proto₧e se jednß o chyby, z nich₧ je velice obtφ₧nΘ se zotavit.
T°φda Exception a jejφ potomci jsou standardnφ chyby, kterΘ u₧ivatel vyvolßvß a oÜet°uje. Na tyto v²jimky se v plnΘ mφ°e vztahuje ustanovenφ p°edchozφho odstavce o tom, ₧e musφ b²t bu∩ chyceny, nebo deklarovßny p°φkazem throws.
T°φda RuntimeException, p°esto₧e je potomkem t°φdy Exception, mß jednu zvlßÜtnost -- v²jimky tohoto druhu nemusφ b²t deklarovßny. Do tΘto kategorie spadajφ toti₧ b∞₧nΘ v²jimky NullPointerException, ArrayIndexOutOfBoundsException a podobn∞, kterΘ by musely b²t deklarovßny prakticky u vÜech metod.
Definovßnφ vlastnφch v²jimek
Pokud naÜe metodu zp∙sobuje n∞jak² nov² druh chyby, m∙₧eme si vytvo°it vlastnφ druh v²jimky. Tento postup je naprosto b∞₧n². StaΦφ vytvo°it potomka t°φdy Exception:
class MojeException extends Exception {
public MojeException() { }
public MojeException(String what) {
super(what); // parametr popisujφcφ chybu
}
}
Mßme-li nov² druh chyby deklarovßn, m∙₧eme jej pou₧φt podobn∞ jako ji₧ existujφcφ v²jimky:
throw new MojeException();
Vytvß°enφ vlastnφch v²jimek je doporuΦen² postup p°i psanφ metod, kterΘ mohou skonΦit ne·sp∞chem. P°φkladem m∙₧e b²t neexistence souboru, nemo₧nost navßzat sφ¥ovΘ spojenφ apod. Ve vÜech t∞chto p°φpadech se hodφ pou₧itφ mechanismu v²jimek.
Abstraktnφ t°φdy
V²hodnou vlastnostφ objektovΘ knihovny je mo₧nost pou₧itφ tzv. abstraktnφch t°φd. Jsou to t°φdy, kterΘ obsahujφ rozsßhl² k≤d, ale k jejich₧ zdßrnΘmu fungovßnφ n∞co chybφ. To n∞co je t°eba doplnit v potomcφch takovΘ t°φdy. Abstraktnφ t°φda obsahuje jednu nebo vφce abstraktnφch metod, tj. metod, kterΘ nemajφ uveden k≤d. Abstraktnφ metody se mohou vyskytovat pouze v abstraktnφ t°φd∞:
abstract class MojeAbstraktni {
abstract void fce();
void pracuj() {
...
fce(); // volßnφ abstraktnφ metody
}
}
Pokud chceme vytvo°it potomka uvedenΘ t°φdy, musφme v n∞m dodat (implementovat) k≤d vÜech abstraktnφch metod:
class Konkretni extends MojeAbstraktni {
void fce() { // deklarace musφ b²t stejnß jako v abstraktnφ metod∞
// implementace fce
}
}
... // na jinΘm mφst∞ v k≤du
Konkretni x = new Konkretni();
x.pracuj(); // volß pracuj zd∞d∞nou od MojeAbstraktni
// z pracuj se volß fce definovanß v Konkretni
Nynφ je t°eba odpov∞d∞t na otßzku, k Φemu nßm m∙₧e b²t pou₧itφ abstraktnφch t°φd a metod dobrΘ. P°i psanφ knihovny autor Φasto pot°ebuje pracovat s prom∞nn²mi n∞jakΘ t°φdy, kterou u₧ivatel knihovny bude upravovat podle svΘho. Autor knihovny tedy nadeklaruje abstraktnφ t°φdu a napφÜe v nφ vÜechny metody, kterΘ m∙₧e napsat on. Metody, u nich₧ neznß jejich implementaci, proto₧e tu bude vytvß°et u₧ivatel knihovny, pouze deklaruje a oznaΦφ jako abstraktnφ. To mu vÜak nebrßnφ tyto abstraktnφ metody ve svΘm k≤du volat. P°i jejich skuteΦnΘm volßnφ bude p°φsluÜnß prom∞nnß ukazovat na konkrΘtnφ objekt, kter² bude mφt vÜechny metody definovßny.
Interface
Jednφm z poslednφch rys∙ jazyka Java, se kter²m se seznßmφme, je pou₧itφ tzv. interfac∙. Je trochu podobnΘ pou₧itφ abstraktnφch t°φd -- op∞t se pou₧φvajφ jako rozhranφ knihovny. Pokud chceme mφt toto rozhranφ jeÜt∞ flexibiln∞jÜφ ne₧ p°i pou₧itφ abstraktnφch t°φd, m∙₧eme prßv∞ deklarovat interface. Interface je vlastn∞ mno₧ina deklaracφ metod a konstant:
public interface Rozhrani {
public void f1();
public int f2(int param1);
}
Pokud mßme interface definovßn, m∙₧eme deklarovat prom∞nnΘ tohoto typu a pou₧φvat je ve svΘm k≤du:
...
void mojeMetoda(Rozhrani x) {
x.f1();
int k = x.f2(10);
...
}
Prom∞nnΘ s typem rozhranφ jsou z hlediska jazyka obyΦejnΘ prom∞nnΘ typu odkazu na n∞jak² objekt. P°i volßnφ metody pou₧φvajφcφ prom∞nnou typu rozhranφ musφme za hodnotu tΘto prom∞nnΘ dosadit odkaz na objekt, kter² tzv. implementuje uvedenΘ rozhranφ. To, ₧e n∞jakß t°φda implementuje rozhranφ, je uvedeno v jejφ deklaraci:
class A implements Rozhrani {
...
public void f1() { ... } // deklarace odpovφdajφcφ Rozhrani
public int f2(int param1) { ... }
}
...
A y = new A(); // vytvo°enφ objektu, kter² implementuje rozhranφ
mojeMetoda(y); // dosazenφ p°φsluÜnΘho objektu do prom∞nnΘ x
Pokud naÜe t°φda implementuje rozhranφ, musφ obsahovat vÜechny metody uvedenΘ v danΘm rozhranφ. Tato vlastnost umo₧nφ fungovßnφ k≤du, ve kterΘm se vyskytovaly prom∞nnΘ typu rozhranφ. V²hodou rozhranφ oproti d∞d∞nφ z abstraktnφ metody je to, ₧e za klφΦov²m slovem implements m∙₧e b²t libovoln² poΦet interfac∙ -- d∞d∞nφ od abstraktnφ t°φdy je omezeno pouze na jednoho rodiΦe.
Prßce s grafick²m u₧ivatelsk²m rozhranφm (GUI)
Standardnφ knihovny jazyka Java obsahujφ n∞kolik balφk∙ pro prßci s GUI. VÜechny tyto knihovny jsou napsßny tak, ₧e jednou napsan² program by m∞l b∞₧et stejn∞ v prost°edφ MS Windows, Xwindows a ve vÜech systΘmech podporujφcφch n∞jak²m zp∙sobem prßci s okΘnky. To znamenß, ₧e podpora pro grafiku musφ b²t integrßlnφ souΦßstφ JVM (Java Virtual Machine). Hlavnφ t°φdy jsou obsa₧eny v balφku java.awt.
AWT -- Abstract Window Toolkit
Tato knihovna obsahuje vÜechny d∙le₧itΘ t°φdy pro prßci s u₧ivatelsk²m rozhranφm. Sklßdß se jednak z n∞kolika abstraktnφch t°φd, kterΘ nßm umo₧≥ujφ vytvß°et vlastnφ elementy u₧ivatelskΘho rozhranφ. Velmi d∙le₧itou Φßstφ jsou naprogramovanΘ vÜechny zßkladnφ elementy u₧ivatelskΘho rozhranφ. Tyto elementy m∙₧eme pak velice jednoduch²m zp∙sobem pou₧φvat ve sv²ch programech.
Rozhranφ AWT prod∞lalo °adu zm∞n od verze 1.0 do verze 1.1. V tomto serißlu si zkusφme p°edstavit rysy, kterΘ jsou spoleΦnΘ vÜem verzφm, dalÜφ diskuse ponechßme na specializovanΘ pojednßnφ.
Pou₧itφ ji₧ hotov²ch komponent u₧ivatelskΘho rozhranφ je velice jednoduchΘ -- tyto komponenty staΦφ p°idßvat do svΘho programu pomocφ p°φkazu add:
import java.awt.*;
import java.applet.*;
public class Test extends Applet{
Button okButton = new Button("OK");
Checkbox c = new Checkbox("Skrt");
TextField t = new TextField(15);
public void init() {
add(okButton);
add(c);
add(t);
}
}
Tento p°φklad p°idß do appletu tlaΦφtko (Button), zaÜkrtßvacφ polφΦko (Checkbox) a vstupnφ °ßdku (TextField). Po p°elo₧enφ, vlo₧enφ do HTML dokumentu a spuÜt∞nφ se v appletu objevφ p°idanΘ komponenty.
V knihovn∞ AWT najdeme n∞kolik abstraktnφch t°φd, od nich₧ jsou odvozeny vÜechny t°φdy reprezentujφcφ konkrΘtnφ elementy GUI. Zßkladnφ t°φda se jmenuje Component. Tato t°φda reprezentuje nejjednoduÜÜφ komponentu, kterß se m∙₧e zobrazovat -- tj. mezi jejφ vlastnosti pat°φ sou°adnice umφst∞nφ, velikost apod. Mezi potomky t°φdy Component pat°φ:
- Button -- reprezentuje tlaΦφtko
- Checkbox -- zaÜkrtßvacφ polφΦko
- List -- seznam s v²b∞rem
- Choice -- °ßdka s v²b∞rem prvk∙
- TextField -- vstupnφ °ßdka
- TextArea -- editaΦnφ plocha (vφce°ßdkov² vstup textu)
- Label -- popiska
Pou₧itφ t∞chto komponent bylo ilustrovßno na uvedenΘm p°φkladu. To znamenß, ₧e nejprve je t°eba pomocφ volßnφ new a konstruktoru vytvo°it p°φsluÜnou komponentu. Tuto lze pak vlo₧it p°φkazem add do tzv. kontejneru, neboli komponenty, kterß m∙₧e obsahovat dalÜφ komponenty. Toto vlo₧enφ musφme provΘst p°ed zaΦßtkem provßd∞nφ programu -- metoda init v appletu se provede p°ed tφm, ne₧ je applet zobrazen.
Kontejner je reprezentovßn objektem, kter² je potomkem t°φdy Container. T°φda Container je rovn∞₧ potomkem t°φdy Component. T°φda Applet je potomkem t°φdy Container, tj. applet je v₧dy kontejner, do kterΘho m∙₧eme vklßdat jednotlivΘ komponenty pomocφ metody add. Metoda add je definovßna ve t°φd∞ Container a vÜechny jejφ potomci ji d∞dφ.
PφÜeme-li applet, m∙₧eme p°idßvat komponenty p°φmo do n∞j. Pokud vÜak pφÜeme samostatnou aplikaci, je pot°eba nejprve vytvo°it okno, kterΘ bude tvo°it hlavnφ okno naÜeho rozhranφ. K tomuto ·Φelu obsahuje knihovna AWT t°φdu Frame. Tato t°φda slou₧φ k vytvß°enφ dalÜφch oken na obrazovce. Objekt t°φdy Frame je rovn∞₧ potomkem Containeru, tj. umo₧≥uje nßm do n∞j vklßdat komponenty podobn∞ jako do appletu. Chceme-li v samostatnΘ aplikaci vytvo°it novΘ okno, m∙₧eme napsat do metody main nap°. toto:
public static void main(String[] args) {
Frame f = new Frame();
f.add(new Button("Ok"));
f.show();
}
V tomto p°φpad∞ jsme vyvolali metodu add na objekty t°φdy Frame. Po zavolßnφ metody show uvidφme na obrazovce novΘ okΘnko s jednφm tlaΦφtkem. Tento program nereaguje na ₧ßdnΘ udßlosti, dokonce ani na pokus o zav°enφ okna. Pokud chceme, aby program n∞co d∞lal, musφme p°idat jeÜt∞ reakce na tvz. udßlosti.
Udßlosti
UvedenΘ p°φklady ukazovaly, jak vytvo°it u₧ivatelskΘ rozhranφ. To vÜak samo o sob∞ nestaΦφ. JeÜt∞ je t°eba napsat k≤d, kter² bude n∞co provßd∞t, nap°. p°i stisknutφ tlaΦφtka. Musφme naprogramovat reakci na udßlosti, kterΘ mohou v programu nastat.
Chceme-li nap°. zareagovat na stisk tlaΦφtka, m∙₧eme p°epsat metodu action u apletu:
import java.awt.*;
import java.applet.*;
public class Test2 extends Applet{
Button okButton = new Button("OK");
public void init() {
add(okButton);
}
public boolean action(Event e, Object what){
if (e.target == okButton) {
SystΘm.out.println("Stisknut button");
}
return true;
}
}
Podobn²m zp∙sobem je mo₧nΘ reagovat na udßlosti, kterΘ vzniknou kliknutφm myÜi, stisknutφm klßvesy na klßvesnici apod. JmΘna metod, kterΘ zpracovßvajφ udßlosti, se bohu₧el zm∞nila od verze 1.0 k verzi 1.1, tak₧e je v₧dy pot°eba dßvat pozor na to, kterou verzi p°ekladaΦe a prost°edφ mßme k dispozici. Verze 1.1 podporuje i volßnφ metod z verze 1.0, tj. zp∞tnß kompatibilita byla zachovßna, musφme vÜak dßvat pozor, abychom nepou₧ili volßnφ z verze 1.1, mßme-li k dispozici pouze verzi 1.0 (nap°. v prohlφ₧eΦi).
Layout Manager
V p°edchozφch p°φkladech jsme si ukazovali, jak p°idßvat komponenty do kontejner∙ pomocφ metody add. P°i prohlφ₧enφ b∞₧φcφch program∙ vßs jist∞ napadlo, ₧e jsme nikdy nezadßvali ₧ßdnΘ sou°adnice, kam se danΘ komponenty majφ zobrazit. To v∞tÜinou nenφ t°eba, nebo¥ v knihovn∞ AWT mßme k dispozici objekty, kterΘ se o umis¥ovßnφ komponent starajφ za nßs. T∞mto objekt∙m se °φkß Layout Managery, proto₧e implementujφ rozhranφ Layout Manager.
Pou₧itφ Layout Manager∙ by se mohlo zdßt na prvnφ pohled zbyteΦnΘ a t∞₧kopßdnΘ -- proΦ bychom nemohli umis¥ovat komponenty na p°esn∞ urΦenΘ sou°adnice? ProblΘm s umis¥ovßnφm na absolutnφ sou°adnice je v tom, ₧e nikdy nevφme, jak bude vypadat fyzickΘ za°φzenφ, na kterΘm se budou naÜe komponenty zobrazovat -- nem∙₧eme v∞d∞t, jakΘ bude mφt u₧ivatel k dispozici rozliÜenφ obrazovky, jak velkΘ budou fonty atd. Layout Manager by nßs jako programßtory m∞l od t∞chto detail∙ odstφnit.
S ka₧d²m kontejnerem (potomkem t°φdy Container) je svßzßn jeden Layout Manager. Pokud jej nezm∞nφme, je pou₧it defaultnφ -- pro t°φdu Panel (a tedy i pro jejφho potomka Applet) je pou₧it FlowLayout.
FlowLayout
Objekt tΘto t°φdy pracuje tak, ₧e umis¥uje jednotlivΘ komponenty za sebou podle po°adφ vklßdßnφ p°φkazem add. Pokud se dalÜφ komponenta nevejde na °ßdek, pokusφ se ji umφstit na dalÜφ °ßdek. Toto chovßnφ je vhodnΘ, pokud do kontejneru umis¥ujeme malΘ mno₧stvφ objekt∙ (nap°. dva Buttony apod.). Pokud chceme tento LayoutManager pou₧φt i v objektech, pro kterΘ nenφ defaultnφ, m∙₧eme pou₧φt p°φkaz:
setLayout(new FlowLayout());
Tento p°φkaz je t°eba vyvolat na p°φsluÜn² kontejner, nap°. na Frame.
GridLayout
Pokud chceme umis¥ovat objekty do m°φ₧ky, m∙₧eme p°φkazem
setLayout(new GridLayout(x,y));
aktivovat nov² GridLayout. Parametry x a y urΦujφ rozm∞ry m°φ₧ky. Podφvejme se na applet, do kterΘho vlo₧φme Φty°i tlaΦφtka:
import java.awt.*;
import java.applet.*;
public class Test2 extends Applet{
public void init() {
setLayout(new GridLayout(2,2));
add(new Button("Prvni"));
add(new Button("Druhy"));
add(new Button("Treti"));
add(new Button("Ctvrty"));
}
}
Po spuÜt∞nφ si m∙₧eme vyzkouÜet, co se bude dφt, budeme-li m∞nit velikost naÜeho appletu.
BorderLayout
DalÜφm z nejb∞₧n∞jÜφch sprßvc∙ je BorderLayout -- umo₧≥uje nßm urΦit, na kterΘm mφst∞ se p°idßvanß komponenta objevφ. Pou₧φvß k tomu variantu p°φkazu add, kterß mß navφc jeÜt∞ jeden argument, kter² je p°edßn Layout Manageru. P°i pou₧itφ tohoto objektu ji₧ nezßle₧φ na po°adφ vklßdßnφ komponent jako v p°edchozφch p°φpadech. Pou₧itφ si op∞t budeme ilustrovat na p°φkladu appletu:
import java.awt.*;
import java.applet.*;
public class Test3 extends Applet{
public void init() {
setLayout(new BorderLayout());
add("North", new Button("Prvni"));
add("South", new Button("Druhy"));
add("East", new Button("Treti"));
add("West", new Button("Ctvrty"));
add("Center", new Button("Ctvrty"));
}
}
Ostatnφ
V knihovn∞ AWT mßme krom∞ ji₧ uveden²ch Layout Manager∙ jeÜt∞ mo₧nost pou₧φt nßsledujφcφ objekty:
- CardLayout -- slou₧φ k vytvo°enφ n∞kolika karet, kterΘ m∙₧eme na obrazovce st°φdat. Je to u₧iteΦnΘ v p°φpadech, kdy chceme m∞nit vzhled rozhranφ za b∞hu.
- GridBagLayout -- tento sprßvce je nejslo₧it∞jÜφ ze standardnφch sprßvc∙. Pomocφ objekt∙ t°φdy GridBagConstraints umo₧≥uje u ka₧dΘ komponenty stanovit vlastnosti p°i zm∞n∞ velikosti kontejneru.
Pokud nechceme pou₧φt ₧ßdn² Layout Manager, m∙₧eme pomocφ p°φkazu:
setLayout(null);
vypnout jejich pou₧φvßnφ. Potom se musφme starat o sou°adnice, na kterΘ danΘ komponenty umis¥ujeme, a proto to nenφ doporuΦovan² zp∙sob psanφ aplikacφ v Jav∞.
Vytvß°enφ GUI
P°i vytvß°enφ u₧ivatelskΘho rozhranφ nemusφme pou₧φvat jeden Layout Manager pro celΘ okno, do kterΘho umis¥ujeme komponenty. M∙₧eme si plochu okna rozd∞lit a v ka₧dΘ Φßsti pou₧φt jin² druh sprßvce rozmφst∞nφ. Rozd∞lenφ plochy okna nßm umo₧nφ nejjednoduÜÜφ potomek t°φdy Container -- Panel.
Panel je komponenta, kterß slou₧φ k umis¥ovßnφ dalÜφch komponent. M∙₧eme jej umφstit do libovolnΘho kontejneru, tj. m∙₧eme vytvo°it hierarchickou strukturu panel∙, kterΘ realizujφ rozmis¥ovßnφ komponent. U ka₧dΘho panelu nastavφme Layout Manager tak, aby celkov² vzhled GUI odpovφdal naÜφ p°edstav∞.
V nßsledujφcφm p°φkladu si ukß₧eme pou₧itφ n∞kolika panel∙ pro lepÜφ rozvr₧enφ komponent v appletu:
import java.awt.*;
import java.applet.*;
public class Test4 extends Applet{
public void init() {
Panel p1, p2, p3;
setLayout(new BorderLayout());
add("East", p1 = new Panel());
add("South", p2 = new Panel());
add("West", p3 = new Panel());
p1.setLayout(new GridLayout(3,1));
p1.add(new Checkbox("Jedna"));
p1.add(new Checkbox("Dve"));
p1.add(new Checkbox("Tri"));
p3.setLayout(new GridLayout(3,1));
p3.add(new Checkbox("Maslo"));
p3.add(new Checkbox("Pivo"));
p3.add(new Checkbox("Rohlik"));
p2.add(new Button("Ok"));
}
}
Tato ukßzka po spuÜt∞nφ zobrazφ applet, ve kterΘm budou jednotlivΘ komponenty umφst∞ny ve skupinßch urΦen²ch jednotliv²mi panely.