Enhavo

Objektema programado per Ĝavo


4 La kernaj elementoj de Ĝavo

Kiu programas per Ĝavo, ne devas verki ĉiujn klasojn mem; la Ĝava Rul-Medio (angle: Java Runtime Environment, JRE) ofertas multajn utilajn klasojn, kiu povas esti rekte uzataj aŭ servi kiel bazoj por derivi proprajn klasojn.

4.1 La bazaj daten-tipoj: nombroj kaj signoj

Ni jam vidis, ke la klasoj de Ĝavo estas grupigitaj en pakaĵoj. Bazaj klasoj troviĝas en la pakaĵo java.lang. Tiu pakaĵo estas ĉiam alirebla. Kompreneble en ĝi troviĝas la klaso Object.

En la pakaĵo java.lang troviĝas ankaŭ klasoj, kies ekzempleroj estas nombroj. La sekva tabelo montras ilin. La klaso Number estas abstrakta superklaso de la aliaj.

NomoPriskribo
Number Abstrakta bazo por la aliaj nombraj klasoj
Integer Entjera nombro kun maksimume 32 bitoj
Byte Entjera nombro kun maksimume 8 bitoj
Short Entjera nombro kun maksimume 16 bitoj
Long Entjera nombro kun maksimume 64 bitoj
Float Glit-koma (frakcia) nombro kun 32 bitoj
Double Glit-koma (frakcia) nombro kun 64 bitoj

Aliaj tre gravaj klasoj reprezentas signojn kaj signo-vicojn:

NomoPriskribo
Character Signo el la baza aro de Unikodo, kun kod-numero malpli granda ol 216
String Signovico unikoda

La klaso String bone helpas pri la manipulado de signovicoj. Eblas kunigi du vicojn per simpla "+"-signo. Notindas, ke tiu plus-operacio ne estas komuteca: El "ba"+"ro" rezultas "baro", ne "roba".

Metodoj de la klaso String serĉas vicojn en aliaj vicoj aŭ eĉ anstataŭigas sub-vicojn per aliaj vicoj. Programistoj tamen ne forgesu, ke tiaj operacioj daŭras des pli longe, ju pli longaj estas la vicoj.

Se iu unikoda signo ne estas disponebla sur klavaro, aŭ se programisto volas verki programon nur el askiaj signoj, eblas anstataŭigi signon per ĝia kvar-cifera dek-ses-uma kodo, enkondukita per \u. Jen ekzemplo:

"E\u0125o\u015Dan\u011Do \u0109iu\u0135a\u016Dde" = Eĥoŝanĝo ĉiuĵaŭde

La nombro-klaso estas malpli komforte uzeblaj ol signo-vicoj: Ne eblas senpere kalkuli pri ili, sed necesas unue preni ilian "nombran valoron". Jena program-peco kalkulas "2+3":

    Integer du = new Integer(2);
    Integer tri = new Integer(3);
    Integer sumo = new Integer(du.intValue() + tri.intValue());
    

Sed la (ne abstraktaj) nombro-klasoj havas fratojn, per kiuj eblas rekte kalkuli. Ili nomiĝas (kiel en la lingvoj C aŭ C++) byte, short, int, long, float, double. Tiuj daten-tipoj tamen ne estas objektoj, do ne apartenas al iu klaso kaj ne posedas metodojn. Kutime oni kalkulas per tiaj datenoj kaj uzas la nombrajn klasojn nur tie, kie nepre necesas vera objekto. Ankaŭ la klaso Character havas tian fraton; ĝi nomiĝas char.

4.2 Buleaj valoroj (jes/ne)

Samkiel aliaj lingvoj Ĝavo havas bulean (vero-valoran) tipon, kiu prenas la valorojn "vera" kaj "malvera" (programe: true kaj false). Ankaŭ ĝi posedas klasan kaj ne-klasas reprezentaĝon. La klaso nomiĝas Boolean, la ne-klasa tipo boolean.

Ĝavo ne interpretas nombrojn kiel vero-valorojn, kiel aliaj lingvoj, kiuj aŭtomate aldonas al nombroj la komparon "ne egala al nulo", kie tio necesas. La esprimo if (172) … en Ĝavo estas erara; necesas skribi if (172 != 0) ….

Ĝavo havas ses komparilojn inter nombroj, kiuj produktas vero-valorojn laŭ jena tabelo:

== egala al
<= malpli al
>= pli al
< malpli ol
> pli ol
!= ne egala al

Por kalkuli pri vero-valoroj ekzistas jenaj operaciiloj:

a && b a kaj b
a || b a kaŭ b (kaj/aŭ)
a ^ b a aŭ b (a malegala al b)
! a ne a (malo de a)
a & b a kaj b, sed kalkulu b nur se necesas
a | b a kaŭ b, sed kalkulu b nur se necesas

La operaciilo = ne estas komparilo, sed valorizas variablojn. Ĝia rezulto estas la nova valoro de la variablo. Tial la uzo de unuobla = anstataŭ duobla == plej ofte havas ne-bulean rezulton kaj kaŭzas sintaksan eraron, kiu estas facile trovebla. Komparu jenan ekzemplon:

       int n = …;
       if (n = 40) { … }  // eraro: 40 ne estas bulea 

Tia eraro en la lingvo C povus kaŭzi seriozajn problemojn, ĉar la nombro 40 estas tie interpretebla ankaŭ kiel bulea valoro.

4.3 Tabeloj

El objektoj de ĉiuj klasoj, kaj ankaŭ el la ne-objektaj nombroj, eblas formi tabelojn, unu- aŭ plur-dimensiajn. Por tio oni uzas ortajn krampojn, simile kiel en C. Ankaŭ tabeloj estas veraj objektoj. Jena ekzemplo montras tabelon el kvin signovicoj:

       String[] nombroj = { "unu", "du", "tri", "kvar", "kvin" };
       int n = 3;
       System.out.println("Mi havas " + nombroj[n] + " komputilo(j)n."); 

En la deklaro de tabelo la ortaj krampoj povas esti post la nomo de la tabelo (kiel en C) aŭ post la tipo (kiel en la ekzemplo). ࣘi-lasta variaĵo pli klare montras, ke la krampoj estas parto de la tipo: Oni povas diri, ke String[] (kun la krampoj) estas la tipo de la tabelo. Tute precize, la tipo (klas-nomo) de la eroj de la tabelo estas java.lang.String, kaj la tipo de la tabelo, teknike, estas [Ljava.lang.String; (kun orta krampo komence kaj punktokomo fine).

Kiel dirite, tabeloj en Ĝavo estas ne simplaj vicoj de objektoj, sed veraj objektoj. Ekzemple ili havas entjeran membron length, nombron, kiu enhavas la longon de la tabelo. Provo aliri tabeleron ekster la tabelo (antaŭu ĝia komenco aŭ post ĝia fino) kaŭzas tujan "escepton"; la programo interrompas sian ruliĝon. Tio ebligas rapide trovi erarojn, kiuj en aliaj lingvoj kaŭzus nur malfrue rimarkeblan misfunkciadon.

Plurdimensiaj tabeloj estas simple tabeloj de tabeloj (de tabeloj…).

4.4 Kolektaĵoj

Kolektaĵoj estas klasoj, kiuj kunigas plurajn iel simil-specajn objektojn laŭ certa maniero. Ekzemploj de kolektaĵoj estas:

Ĝavo ofertas klasojn por tiaj kolektaĵoj. Ili grandparte troviĝas en pakaĵo java.util. Tiuj kolektaĵoj estas tre zorge realigitaj kaj tial pli bonkvalitaj ol klasoj, kiujn oni normale realigus por siaj programoj.

Eble la plej ofte uzata kolektaĵo estas la klaso java.util.ArrayList. Ĝi estas listo, kiu kondutas simile kiel tabelo; ekzemple ĉiu ero de la listo estas egale rapide alirebla. Kontraŭe al vera tabelo ĝi povas kreski; ĝi povas kolekti erojn, kies nombro komence ne estas konata.

4.5 Esceptoj

Ĵus ni menciis, ke erara situacio en programo kaŭzas "escepton". En Ĝavo tio havas precizan signifon: La ruliĝo de la programo estas interrompita kaj povas rekomenciĝi nur, se ie la escepto estas "kaptita". La "kaptejo" devas interpreti la eblajn kaŭzojn de la escepto kaj krei situacion, en kiu la programo povas funkcii plu. Se ne estas alia eblo, ĝi povas eligi mesaĝon pri la escepta situacio kaj fini la programon.

Esceptoj estas potenca mekanismo por trakti neatenditajn situaciojn en programo. Tradicie subprogramoj redonis iujn specialajn valorojn, se ili renkontis tian situacion; ekzemple la nombron "−1", se normalaj rezultoj estas ĉiam pozitivaj. Se la vokanto de tia subprogramo ne volas aŭ ne povas trakti tiun okazon, ĝi devas propagi (pludoni) la informon. Tio estas kompleksa mekanismo malfacile superrigardebla.

Esceptoj kvazaŭ preteriras la normalan vojon de rezult-redono: Escepto, kiu okazas en iu metodo, povas rekuri laŭ la tuta ĉeno de metodoj, kiuj ĝin vokis. Nur eksplicita kapto de escepto povas "haltigi" ĝin.

La jena ekzemplo montras metodon, kiu dividas du nombrojn. Tio eblas nur, se la dividanto ne estas nulo. Se ĝi estas nulo, Ĝavo aŭtomate konstatas la situacion kaj "ĵetas" koncernan escepton. La unua metodo kaptas la escepton kaj eligas mesaĝon, la dua pludonas ĝin.

       /**
        * dividu entjeran nombron per alia entjera nombro.
        * se la dividanto estas ne-nula, redonu la kvocienton;
        * alie eligu mesaghon kaj redonu 0.
        */
       int dividu1(int dividato, int dividanto) {
           try {
               return dividato / dividanto;
           } catch (DivideByZeroException escepto) {
               System.err.println("Vi provis dividi per nulo.");
               return 0;     // necesas redoni iun rezulton
           }
       }

       /**
        * dividu entjeran nombron per alia entjera nombro.
        * se la dividanto estas ne-nula, redonu la kvocienton;
        * alie (plu-)jhetu escepton.
        */
       int dividu2(int dividato, int dividanto)
           throws DivideByZeroException {
           return dividato / dividanto;
       }

La unua ekzemplo montras, ke oni povas kapti escepton en bloko inter la ŝlosil-vortoj try (provu) kaj catch (kaptu). Normale la bloko post catch ne estas plenumata; la programo eniras ĝin nur por trakti escepton. La dua ekzemplo ne havas tian blokon; ĝi do simple pluĵetas esceptojn. Por tio ĝi devas deklari (throws DivideByZeroException), ke tio povas okazi. Tiel vokanto de tia metodo povas prepariĝi al esceptoj. (Tute precize, DivideByZeroException apartenas al baza aro da esceptoj, kiujn ne necesas deklari; sed tio ne gravas tie ĉi.) Se okazas escepto kaj neniu metodo kaptas ĝin, la escepto haltigas la tutan programon.

Oni ne uzu esceptojn anstataŭ simpla alternativo, sed nur por vere "esceptaj" situacioj, kiuj okazas tre malofte.

Ni vidis, ke Ĝavo posedas apartan escepton por "divido per nulo"; ĝi nomiĝas DivideByZeroEsception. Tiu kaj multaj aliaj klasoj estas subklasoj de Exception. Programantoj povas mem derivi plian esceptojn.


sekva leciono