Praktikum nr. 2

Teemad

Eclipse. Java põhikonstruktsioonid: valikulaused, tsüklid. Staatilised meetodid. Signatuur. Java massiiv.

Pärast selle praktikumi läbimist üliõpilane

Keskkond Eclipse

Esimeses praktikumis kirjutasime programme tavalise lihtsa tekstiredaktoriga ja kompileerisime ning käivitasime käsurealt. Alguses on see õpetlik - näeme, et programmi tekst ongi lihtsalt tekst ja programmi töölepanemisel on mitu etappi. Kui aga teha rohkem ja suuremaid programme, siis on mõistlik kasutada spetsiaalseid keskkondi, mis osa tööd programmeerija eest "ära teeb". Käesoleva kursuse nö. ametlik keskkond on Eclipse. Lubatud on kasutada ka teisi keskkondi, aga neid eraldi ei tutvustata.

Eclipse on vabavara, mis töötab erinevatel operatsioonisüsteemidel (Windows, Linux, Solaris ..). Eclipse toetab erinevates programmeerimiskeeltes töötamist (Java, PHP, C++, ...), samuti saab tema abil töötada nt. UML-ga. Eclipse evib hästi palju võimalusi. Meie vaatleme (ja esialgu vajame) neist vaid osasid.

Meie tegutseme Java projektidega. Põhieesmärgiks on algtekstide toimetamine, süntaktiline silumine ja käivitamine. Silumise hõlbustamiseks saab panna katkestuspunkte, mille abil saab jälgida muutujate väärtuste muutumist. Teie andmed asuvad töökataloogis (workspace). Sinna peab kasutajal olema õigus kirjutada. Meie puhul sobib selleks nt. Z:-ketta alamkataloog oop. Iga projekt paigutub eraldi alamkataloogi, milles siis on erinevad failid.

Ülesanne A1

Püüdkem nüüd Eclipse käivitada
Eclipse'i koduleht on http://www.eclipse.org. Ingliskeelse juhendi leiab vastavalt aadressilt http://help.eclipse.org/juno/index.jsp või http://help.eclipse.org/indigo/index.jsp.

Ülesanne A2

Harjutame nüüd aritmeetilisi tehteid. Alustame täisarvuliste (int-tüüpi) muutujatega. Täisarvuliste muutujatega saab teha mitmesuguseid tehteid, millest siinkohal tulevad vaatluse alla
Enamasti toimivad need nii nagu matemaatikas kombeks, kuid on ka erinevusi. Koostage programm nende tehete omadustega tutvumiseks. Selleks kirjeldage täisarvulised muutujad a, b, c, d, e ning omistage muutujale a väärtus 2147483647, ülejäänute väärtused valige ise.

   1. Liitke, lahutage ja korrutage muutujate väärtusi.
   2. Mis saab siis, kui muutuja a väärtusele liita 1? Kuidas seda tulemust seletada?
   3. Leidke jagatised 9/3, 7/4, -3/2, 3/(-2), -7/(-3).
   4. Proovige leida jääki nii positiivsetest kui ka negatiivsetest arvudest.

Tulemused väljastage näiteks nii:

    System.out.println("Arvude " + b + " ja " + c + " summa on " + (b + c));

Mis muutub, kui avaldises (b + c) sulud ära jätta?

Kui täisarvudega on lihtsam, siis ujukomaarvudega on ka Javas täpsuse küsimus. Proovige näiteks System.out.println(1-0.9);

Ülesanne P1

Kirjutage lühidalt paberile,

Klass Math 

Kui on vaja ümardada, siis saame kasutada meetodit Math.round. Näiteks Math.round(0.7) või Math.round(m1), kui m1 on enne väärtustatud. Klass Math on juba valmis tehtud ja vajadusel saame selle klassi meetodeid kasutada. Näiteks juhusliku reaalarvu leidmiseks võib kasutada meetodit Math.random, mis tagastab (pseudo)juhusliku double-tüüpi arvu poollõigust [0,0; 1,0) (st 0 on kaasa arvatud, 1 aga mitte). Omistamisdirektiivi
double juhuarv = Math.random();
täitmisel omandab muutuja  juhuarv konkreetse väärtuse, mida me ette ei tea. Kui on vaja juhuslikku arvu mingis teises poollõigus, siis saab selle moodustada korrutamise ja liitmise abil. Näiteks avaldise  Math.random()*10+20 väärtus ei ole väiksem kui 20.0 ja on väiksem kui 30.0.

Kui  on tarvis juhuslikku täisarvu, siis oleks sobivaks teeks näiteks reaalarvulise juhusliku arvu ümardamine. Tähele tuleb panna, et Math.random tagastab double-tüüpi arvu, kuid Math.round tagastab double-tüüpi argumendi puhul long-tüüpi täisarvu. Niisiis toimivad kenasti järgmised omistamised:

double reaalJuhuarv= Math.random()*5+15;
long longJuhuarv = Math.round(Math.random()*5+15);

Kui aga tahaksime int-tüüpi juhuslikku arvu, siis on vajalik tüübiteisendus:

int taisJuhuarv = (int)Math.round(Math.random()*5+15);

Ülesanne P2

Proovige, kas eeltoodud variandid toimivad ja annavad vajalikud juhuarvud. Proovige ka varianti
int valeJuhuarv = (int)Math.random()*5+15;
ning kommenteerige paberil viimase näite olemust.


Selleks, et teada saada, millised on mingi konkreetse klassi Math meetodi võimalikud argumentide ja tagastatavate väärtuste tüübid ja millised meetodid klassis Math üldse olemas on, avage Java API veebileht ja otsige raamist All classes üles link Math. Pärast klassi üldist tutvustust on väljade ja meetodite loetelu koos lühitutvustustega.  Näeme, et mõningaid meetodinimesid on korduvalt, näiteks abs on neli korda ja round kaks korda. Tõesti saab ühes klassis olla mitu samanimelist meetodit. Aga ainult sellisel juhul, kui formaalsete parameetrite (argumentide) arvus ja/või tüüpides on erinevused.  Antud juhul siis on eraldi meetodid absoluutväärtuse leidmiseks double-, float-, int- ja long-tüüpi argumendi jaoks. Ja erinevused on siis ka tagastustüübis. Meetodi iseloomustust, mis koosneb meetodi nimest ja formaalsete parameetrite tüüpide loetelust, nimetatakse signatuuriks. Niisiis ühes klassis peavad meetodite signatuurid olema erinevad.

Ülesanne A3 --> P3

Proovige erinevate klassi Math meetodite tööd. Proovige mõnda ühe ja mõnda kahe argumendiga meetodit. Proovige ka kasutada väärtusi e ja π. Proovige ja tutvuge ka vähemalt ühe sellise funktsiooniga, millest te varem kuulnud pole. Palun kirjutage paberile, mis meetoditega tutvusite ja mida teeb teile seni võõras olnud funktsioon.

Teeme ise meetodeid

Kuigi olemasolevates sadades klassides on tuhandeid meetodeid, mida saame kasutada, peame oskama ka ise neid juurde teha. Selles praktikumis piirdume staatiliste meetoditega (nimetatakse ka klassimeetoditeks). Eristatavad on need seeläbi, et piiritlejaks on võtmesõna static. Kui vaadata klassi Math meetodeid, siis need on kõik staatilised, mida kasutades tuleb meetodi ette lisada klassinimi. Staatiline on ka peameetod main.

Signatuur on meetodi (või konstruktori) iseloomustus, mis koosneb nimest ning formaalsete parameetrite tüüpide loetelust. Näiteks kui meetodi päis on static double korrutaKolmArvu (double a, double b, double c), siis signatuur on korrutaKolmArvu(double, double, double).

Meetodi igal väljakutsel väärtustatakse vastavate argumentide väärtusega muutujad (formaalsed parameetrid), mis on meetodi formaalsete parameetrite loetelus kirjeldatud. Need muutujad on kasutatavad ainult selle meetodi kehas. Rõhutame, et kui meetod ei tagasta väärtust, siis on tagastustüübiks void (tühitüüp). Kõigil teistel juhtudel aga peab tagastustüüp olema kooskõlas sellega, mis tüüpi väärtuse meetod tagastab. Tagastatakse aga selle avaldise väärtus, mis asub naasmisdirektiivis võtmesõna return järel.

Meetodeid võib ühes klassis olla mitmeid, näiteks järgmises klassis on kolm meetodit, kõik staatilised.

class KolmArvu {
    static double korrutaKolmArvu(double a, double b, double c){
        return a*b*c;
    }

    static void valjasta(double a, double b, double c){
        System.out.println("Antud arvud: " + a + ", " + b + ", " + c);
    }

    public static void main(String[] args) {
        double x = 1.5;
        double y = 2.25;
        double z = 3;
        valjasta(x, y, z);
        System.out.println("Nende korrutis: " + korrutaKolmArvu(x, y, z));
    }
}

Ülesanne A4 --> P4

Ühes klassis võivad olla ka samanimelised meetodid. Koostage kolm samanimelist (aga muidugi erineva signatuuriga) staatilist meetodit, mis vastavalt sellele, millist tüüpi ja kui palju on argumente, tagastaksid erinevat tüüpi väärtuse. Näiteks

Rakendage loodud meetodeid peameetodis. Palun kirjutage paberile koostatud meetodite signatuurid. 


Järjend, massiiv

Lineaarselt järjestatud hulka nimetatakse jadaks, igal jada elemendil on tema järjenumbrit näitav indeks.  Järjendiks nimetatakse lõplikku jada. Programmeerimises on järjendeid sageli vaja ning nende kujutamiseks sobib ühemõõtmeline massiivJavas saab järjend (massiiv) koosneda vaid üht tüüpi elementidest. Võime rääkida näiteks täisarvude massiivist. Kuna järjend ja massiiv on tihedalt seotud, siis järgnevas tekstis ongi neid käsitletud praktiliselt sünonüümidena.

Näitena vaatleme viieelemendilist järjendit a, mis koosneb Javas elementidest

a[0]
a[1]
a[2]
a[3]
a[4]

Kõik elemendid on ühte ja sama tüüpi, arvu nurksulgudes (järjekorranumbrit) nimetatakse elemendi indeksiks. Iga element võib sõltumata teistest elementidest sisaldada korraga ühte väärtust – täisarvujärjendi puhul täisarvu, reaalarvujärjendi puhul reaalarvu jne, ning igale elemendile võib väärtuse omistada teistest elementidest sõltumatult. Javas kehtib reegel, et järjendi elementide numeratsioon algab alati nullist. Kui järjendis on kokku n elementi, siis viimase elemendi indeks on n-1. Niisugust ühekohalist „nihet“ võrreldes harjunud loendamisviisiga tuleb Java-programmide koostamisel silmas pidada. (Järjendi elemendid võivad olla ka ise järjendid (kõik siis muidugi üht tüüpi järjendid, küll aga võib nende pikkus olla erinev). See lause on lugemiskontrolliks - palun kirjutage paberile ennustus, kas Eesti Vabariigi aastapäeval esineb sademeid ja kui, siis milliseid. Sellisel juhul kujutatakse seda Javas kahemõõtmelise massiivina ja konkreetsele elemendile viidatakse kahe indeksiga, nt. b[2][3].)

Järjendi kirjeldamiseks (programmis kasutuselevõtmiseks) on mitu võimalust, kõige lihtsam on seda teha massiivialgati abil. Näiteks kui oleme viiel päeval lugenud kraadiklaasilt temperatuuri väärtused

10 9 12 11 8
siis võime nendest moodustada järjendi
int[] a = {10, 9, 12, 11, 8};
Niisuguse kirjelduse toimel võtab arvuti kasutusele täisarvujärjendi a ja omistab selle elementidele järjestikku loogelistes sulgudes antud väärtused (seega näiteks a[0] väärtuseks saab 10 ja a[1] väärtuseks 9). Ühtlasi määratakse kindlaks järjendi elementide arv ehk massiivi pikkus, mis saab konstandi a.length väärtuseks (praegusel juhul on selleks 5). (Tegelikult on lubatud ka nurksulgude teine paigutus: int a[] = ...)

Teine võimalus järjendi kasutuselevõtuks on massiiviloome. Eelneva näitega samaväärse tulemuse saame ka nii, et moodustame kõigepealt käsuga

int[] a = new int[5];
tühja viieelemendilise järjendi. Selle kõigi elementide algväärtuseks määratakse automaatselt 0. Seejärel omistame elementidele nullide asemel vajalikud väärtused: 
a[0] = 10;
a[1] = 9;
a[2] = 12;
a[3] = 11;
a[4] = 8;

Järjendi ühe elemendi väärtust võib väljastada standardsel viisil nagu iga teise muutuja väärtust. Näiteks korraldus

System.out.println("Jarjendi esimene element on " + a[0]);
väljastab ekraanile järjendi a (tavamõttes) esimese elemendi väärtuse.

Järjend ja tsükkel

Järjendi kõigi väärtuste väljastamiseks tuleb terve järjend, alates algusest, elementhaaval läbida. Väga sobiv vahend selleks on for-tsükkel, millel on siinkohal isegi kaks varianti. Näiteks programmilõik

for (int i = 0; i < a.length; i++) {
    System.out.println(a[i]);
}
teeb just vajalikku tegevust – igal sammul, kui tsüklimuutuja i saab järjekordse väärtuse, trükitakse ekraanile järjekordse indeksiga element. Muutuja i algväärtus on 0 ja lõppväärtus ühe võrra väiksem kui järjendi pikkus. Indeksi nihke tõttu vaadatakse läbi parajasti kõik järjendi elemendid esimesest viimaseni. Näiteks 5-elemendilise järjendi puhul on:
i väärtus

ekraanile
väljastatakse
märkus

0 10 a[0] väärtus
1 9 a[1] väärtus
2 12 a[2] väärtus
3 11 a[3] väärtus
4 8 a[4] väärtus
5
tsükli lõpp

Nagu eelnevast programmitekstist näha, võib elemendi indeksiks olla ka muutuja. Kui muutuja i on saanud mingi väärtuse, siis on arvutil lihtne kindlaks teha, millist elementi järjendis tähistab a[i]. Samamoodi võib indeksis esineda keerukam aritmeetiline avaldis, sel juhul arvutab arvuti kõigepealt välja avaldise väärtuse ja siis otsib järjendist üles vastava järjenumbriga elemendi.

Kui on vaja järjend läbida nii, et erinevaid elemente pole vaja võrrelda, asendada või eemaldada, siis on kasutatav for-tsükli nö. for-each versioon. Ülaltoodud lõigu saab siis kirja panna ka nii:

for (int elem : a){
    System.out.println(elem);
}

Ülesanne A5 --> P5

Paberile kirjutada harmoonilise keskmise definitsioon ja koht, kust te selle leidsite (kui te seda peast ei teadnud).

Ülesanne P6 <-->A6

Palun kirjutage paberile ilma arvutis katsetamata, mida väljastatakse ekraanile.
int[] jarj1 = {1, 3, 6};
int[] jarj2;
jarj2 = jarj1;
System.out.println(jarj1[1]);
System.out.println(jarj2[1]);
jarj2[1]=4;
System.out.println(jarj2[1]);
System.out.println(jarj1[1]);

Katsetage arvutis ja analüüsige oma vastust.

Sõnejärjend

Kui vaadata peameetodit, siis selle signatuuris paistab ka järjend - sõnejärjend. Peameetodil ongi formaalne parameeter, mille nime võib vabalt valida, aga sageli on selleks valitud args. Tegelikult saamegi selle abil kasutada käsurealt saadud sõnesid. Näiteks kui programm käivitada käsurealt:
java Klassinimi Tartu Riia

saame sõnejärjendi, mille pikkus on kaks ja tema elemendid omandavad väärtused args[0] = "Tartu", args[1] = "Riia".  Käsurealt saadud elemendid on sõned. Kui tahame neid kasutada näiteks täisarvudena või reaalarvudena, peame neid enne teisendama. Selleks saab kasutada vastavates mähisklassides olevaid meetodeid. Näiteks sõne täisarvuks teisendamisel on kasutatav meetod Integer.parseInt, mille argumendiks tulebki sõne anda. Näiteks oletame, et kui käsureal on argumendiks reaalarv 1.67 (java Matkajapikkus 1.67), siis
double pikkus = Double.parseDouble(args[0]);

muudab selle reaalarvuks, millega saame tehteid teha.

Kui kasutame Eclipse'it, siis saame käsurea argumente anda valides Run-menüüst Run Configurations... --> (x)=Arguments ja sisestades argumendid kasti Program arguments.

Kui on vaja luua sõnejärjendit programmi sees, siis toimub nii järjendi loomine kui ka tema elementidele väärtuste samamoodi kui arvujärjendite korral:
String[] sõned = {"Ilus", "punane", "maasikas"};
või

    String[] sõned = new String[pikkus];


Ülesanne A7 --> P7

Koostage programm, mis saab käsurealt inimese eesnime, kehamassi (kilogrammides, täisarvuna) ja pikkuse (meetrites, reaalarvuna) ning arvutaks tema kehamassiindeksi ning annaks vastavalt tulemusele vähemalt kolme sorti soovitusi (näiteks, "Söö rohkem", "Kasva pikemaks" jms.)

Kirjutage paberile, millised on käsurealt lugemise ja eelmises praktikumis käsitletud klaviatuurilt sisestamise põhilised erinevused. Milliste programmide puhul võiks neid rakendada?

Kodune ülesanne!