COMPUTERWORLD
Specializovan² t²denφk o v²poΦetnφ technice
o Internetu
(CW 50/97)

Programovßnφ v jazyce Java

David ètrupl

╪et∞zce

╪et∞zce se pou₧φvajφ k uklßdßnφ textov²ch konstant. Syntaxe pou₧itφ °et∞zc∙ je podobnß jazyku C, ale stejn∞ jako u polφ (a mo₧nß jeÜt∞ vφce) jsou i zde rozdφly. Vypl²vajφ z toho, ₧e °et∞zce jsou v Jav∞ objekty. Tedy na rozdφl od jazyka C °et∞zce v Jav∞ nejsou pole znak∙.

Pro prßci s °et∞zci jsou v balφku java.lang dv∞ t°φdy: String a StringBuffer. Prvnφ z nich slou₧φ k uklßdßnφ konstantnφch °et∞zc∙ a jsou s nφ spojeny novΘ syntaktickΘ konstrukce. T°φda StringBuffer je obyΦejnß pomocnß t°φda slou₧φcφ k manipulaci s jednotliv²mi znaky °et∞zce.

Do prom∞nn²ch typu String lze p°i°azovat textovΘ (String) konstanty:

String s = "Ja jsem String";

Zßpis s uvozovkami vlastn∞ vytvß°φ nov² objekt t°φdy String. Na tento objekt lze vyvolßvat vÜechny metody definovanΘ ve t°φd∞ String stejn∞ jako na p°φsluÜnou prom∞nnou:

String t = s.toUpperCase();


String u = "hele".toUpperCase(); // povolen² zßpis

Objekty t°φdy String majφ konstantnφ hodnotu, tj. °et∞zec jednou ulo₧en² do prom∞nnΘ t°φdy String u₧ nelze m∞nit. Metody, kterΘ zdßnliv∞ m∞nφ hodnoty jednotliv²ch znak∙, v₧dy vytvß°ejφ nov² objekt t°φdy String s p°φsluÜn²mi zm∞nami. D∙vodem pro toto (na prvnφ pohled nepochopitelnΘ) chovßnφ °et∞zc∙ je to, ₧e konstantnφ °et∞zce jsou pou₧φvßny ve virtußlnφm stroji k uklßdßnφ jmen, a jsou tedy v class souborech uklßdßny zvlßÜtnφm zp∙sobem. B∞₧nΘmu programßtorovi nemusφ konstantnost °et∞zc∙ vadit, proto₧e jejich pou₧itφ v programu je relativn∞ jednoduchΘ a pochopitelnΘ.

JedinΘ, na co musφme dßvat p°i pou₧itφ objekt∙ t°φdy String pozor, je porovnßvßnφ °et∞zc∙. Pou₧itφ operßtoru == toti₧ v∞tÜinou nep°inese k²₧en² efekt, proto₧e tento operßtor u typu reference na objekt (jφm₧ typ String bezesporu je) porovnßvß odkazy a nikoliv obsahy p°φsluÜn²ch objekt∙. Chceme-li porovnat dva °et∞zce, pou₧ijeme nejpravd∞podobn∞ji metodu equals nebo equalsIgnoreCase:

String s = "ahoj";


if (s.equals("ahoj"))


//b∞₧n² test na stejnost °et∞zc∙


...


if ("ahoj".equals(s))


// i toto je sprßvn² zßpis


...


Krom∞ pou₧itφ textov²ch konstant je s typem String spojena jeÜt∞ jedna zvlßÜtnost -- lze na n∞j pou₧φt operßtor +. P°i sΦφtßnφ °et∞zc∙ dojde k jejich z°et∞zenφ. V²sledkem souΦtu je nov² °et∞zec s p°φsluÜn²m obsahem. Operßtor + lze pou₧φt bu∩ samostatn∞, nebo v kombinaci s p°i°azenφm:

String t = "Ja jsem" + "retez";


t += "ec"; // p°ipojenφ na konec °et∞zce

Ani p°i jednom ze zp∙sob∙ zßpisu vÜak nedochßzφ k modifikaci ₧ßdnΘho ze sΦφtan²ch °et∞zc∙, proto₧e, jak ji₧ bylo °eΦeno, typ String reprezentuje konstantnφ °et∞zce. V₧dy je naalokovßn nov² objekt a vrßcen odkaz na n∞j. Pokud chceme sami m∞nit hodnoty jednotliv²ch znak∙, pou₧ijeme t°φdu StringBuffer. Objekty t°φdy String m∙₧eme p°evΘst na objekty t°φdy StringBuffer pomocφ konstruktoru t°φdy StringBuffer, kter² mß jako parametr String:

String s = "retez";


StringBuffer sb = new StringBuffer(s);

S prom∞nnou t°φdy StringBuffer m∙₧eme potom pracovat pomocφ jejφch metod, kterΘ p°istupujφ k jednotliv²m znak∙m nebo n∞jak²m jin²m zp∙sobem modifikujφ obsah °et∞zce. P°evod zp∞t do podoby °et∞zce je mo₧n² nap°. pou₧itφm metody toString().

Chyby

Nynφ se podφvßme na to, jak si prost°edφ Javy poradφ s chybami, kterΘ mohou nastat p°i b∞hu aplikacφ. Jednotn²m mechanismem uplat≥ovan²m jak v prost°edφ virtußlnφho stroje Java (JVM), tak v u₧ivatelsk²ch programech, je mechanismus zpracovßnφ v²jimek. Zßkladnφ principy byly op∞t p°evzaty z jazyka C++.

Zpracovßnφ v²jimek

S v²jimkami jste se mohli setkat, pokud se vßm n∞co nepoda°ilo a vßÜ program skonΦil svoji Φinnost p°edΦasn∞ ohlßÜenφm n∞jakΘ chyby. Pokud se vßm n∞co takovΘho stalo, jist∞ jste si vÜimli, ₧e prost°edφ Javy vypφÜe nap°. toto:

Exception occurred during event dispatching:

java.lang.NullPointerException


at java.io.File.<init>(File.java:108)


at MujProgram.handleEvent(MujProgram.java:103)


at java.awt.Window.postEvent(Window.java:409)


at java.awt.MenuComponent.postEvent(MenuComponent.java:128)


at java.awt.MenuComponent.dispatchEventImpl(MenuComponent.java:156)


at java.awt.MenuComponent.dispatchEvent(MenuComponent.java:138)


at java.awt.EventDispatchThread.run(EventDispatchThread.java:65)


Ka₧dß chyba (v²jimka, Exception) je reprezentovßna jednφm objektem. Tento objekt je vytvo°en v mφst∞, kde k chyb∞ doÜlo. Z objektu reprezentujφcφho chybu se m∙₧eme dozv∞d∞t, o jakou chybu se jednß a kde nastala. T°φda, kterß reprezentuje chyby, je potomkem t°φdy Throwable.

To vÜak nenφ vÜechno. Pokud vφme, ₧e n∞kterß Φßst programu m∙₧e zp∙sobit v²jimku, m∙₧eme Φßst k≤du oznaΦit jako tzv. chrßn∞n² blok a na konci tohoto bloku urΦit, co se mß provΘst, pokud p°i jeho provßd∞nφ dojde k v²jimce. Strß₧en² blok zapφÜeme pomocφ p°φkazu:

try {


// strß₧en² blok


} catch (ExceptionType1 name1) {


// k≤d zpracovßvajφcφ chybu 1


} catch (ExceptionType2 name2) {


// k≤d zpracovßvajφcφ chybu 2


}

P°φkazy strß₧enΘho bloku jsou provßd∞ny postupn∞ -- stejn∞ jako v ka₧dΘm jinΘm bloku p°φkaz∙. Pokud vÜak nastane b∞hem jejich provßd∞nφ n∞jakß v²jimka, bude se jejφ typ porovnßvat s typem uveden²m v klauzuli catch. Provßd∞nφ strß₧enΘho bloku je p°eruÜeno a nastßvß zpracovßnφ chyby. Klauzule catch jsou probφrßny postupn∞, p°i nalezenφ prvnφ, kterß dokß₧e chybu zpracovat, je proveden blok jejφho k≤du. Po provedenφ k≤du zpracovßvajφcφho chybu v²poΦet pokraΦuje dalÜφm p°φkazem za p°φkazem try.

Porovnßvßnφ typ∙ v²jimek probφhß tak, ₧e typ uveden² za catch musφ b²t bu∩ stejn² jako typ chyby, nebo musφ b²t jejφm p°edkem v hierarchii d∞d∞nφ. Pokud se typ chyby nevyskytuje v ₧ßdnΘ z klauzulφ catch, chyba nebyla p°φkazem try oÜet°ena a provßd∞nφ celΘho bloku, ve kterΘm se p°φkaz try nachßzφ, skonΦφ ne·sp∞chem. V bloku klauzulφ catch m∙₧e b²t jeden p°φkaz finally, jeho₧ blok se provede v₧dy p°i ukonΦenφ strß₧enΘho bloku -- v p°φpad∞, ₧e nastala chyba, i v p°φpad∞, ₧e ₧ßdnß nenastala:

try {


// strß₧en² blok


} catch (ExceptionType1 name1) {


// k≤d zpracovßvajφcφ chybu 1


} finally {


// k≤d proveden² v₧dy p°ed opuÜt∞nφm bloku


}

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.


| <<< | COMPUTERWORLD | IDG CZ homepage |