Praktikum 2
Teemad
IDE. Java põhikonstruktsioonid: valikulaused, tsüklid. Staatilised meetodid. Signatuur. Java massiiv.
Pärast selle praktikumi läbimist üliõpilane
- oskab arenduskeskkonna (IDE) abil koostada ja käivitada elementaarseid Java programme;
- oskab sooritada aritmeetilisi tehteid;
- oskab kasutada klassi
Math
meetodeid; - teab, mis on formaalsed parameetrid, meetodi tüüp, naasmisdirektiiv;
- oskab koostada ja kasutada lihtsaid staatilisi meetodeid (sh. samanimelisi);
- tunneb ühemõõtmelise massiivi ülesehitust;
- oskab massiivi erineval moel luua ja selle elementidele uusi väärtusi anda;
- oskab koostada ja rakendada lihtsamaid meetodeid massiivi uurimiseks ühekordse tsükli abil;
- oskab käsurealt saadud andmeid kasutada neid vajadusel teisendades sobivasse algtüüpi.
Raudvara
Materjali läbitöötamisel võiks olulise info oma raudvaralehele kirjutada. Nii jäävad olulised asjad paremini meelde!
Arenduskeskkond (IDE)
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äesoleval kursusel kasutame keskkondi Eclipse ja IntelliJ. Lubatud on kasutada ka teisi keskkondi, aga neid eraldi ei tutvustata.
Arenduskeskkondade Eclipse ja IntelliJ sissejuhatus ja nendes projekti loomise juhend asub juba eelmises praktikumis mainitud IDE juhendite lehel.
Ülesanne A1
Looge oma valitud arenduskeskkonnas uus Java projekt. Kirjutage ja käivitage seal programm, mis midagi lihtsat ekraanile kuvab. Vaata lisaks juhendit Eclipse ja IntelliJ jaoks.
Nii Eclipse kui ka IntelliJ jaoks on olemas täitsa mõistlikud inglisekeelsed juhendid: http://help.eclipse.org/
ja https://www.jetbrains.com/help/idea/
.
Ü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
- liitmine (
+
) - lahutamine (
-
) - korrutamine (
*
) - jagamine (
/
) - jäägi leidmine (
%
)
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.
- Liitke, lahutage, korrutage, jagage muutujate väärtusi. Leidke ka jääki.
- Mis saab siis, kui muutuja
a
väärtusele liita1
? Kuidas seda tulemust seletada?
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 (kontroll)
Võtke tühi paber või looge mingi dokument ja kirjutage lühidalt paberile või dokumenti,
- miks
2147483647
ühte liites just selline vastus tuli; - miks on
1-0.9
vastus selline.
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 (kontroll)
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
(see peab olema klass java.lang.Math, mitte pakett java.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. Sellist olukorda nimetatakse üledefineerimiseks (ingl.k overloading). 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 (ingl.k signature). Niisiis ühes klassis peavad meetodite signatuurid olema erinevad.
Ülesanne A3 --> P3 (kontroll)
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/dokumenti, 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
.
'Static' tähendab seda, et meetod pole seotud ühegi objektiga ja saab kõik vajalikud andmed oma parameetrite kaudu, näiteks Math.max(1, 10)
(on siiski ka erandeid). Mittestaatilised meetodid on seotud kindlate isenditega (objektidega) ja nendes olevate andmetega. Selliseid meetodeid nimetatakse ka 'isendimeetoditeks'. Näiteks sõne pikkuse leidmiseks peab kutsuma meetodi 'length' mingi konkreetse sõne küljes: "abc".length()
. Isenditest (objektidest) räägitakse täpsemalt juba järgmises praktikumis.
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, sel korral 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 (kontroll) --> P4 (kontroll)
Ü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
- kui argumentideks on kaks
int
-tüüpi arvu, siis tagastatakse nende summa; - kui argumentideks on üks
double
-tüüpi arv, siis tagastatakse selle arvu ruut, mis on ümardatud täisarvuks; - kui argumentideks on sõne ja
int
-tüüpi arv, siis ei tagastataks midagi, aga see sõne väljastatakse ekraanile vastav arv kordi (kasutage tsüklit).
Rakendage loodud meetodeid peameetodis. Palun kirjutage paberile/dokumenti koostatud meetodite signatuurid.
Järjend ehk massiiv
Lineaarselt järjestatud hulka nimetatakse jadaks, igal jada elemendil on tema järjenumbrit näitav indeks. Järjendiks nimetatakse lõplikku jada. Javas kutsutakse järjendit massiiviks (ingl.k array) ja see saab koosneda vaid üht tüüpi elementidest. Võime rääkida näiteks täisarvude massiivist.
Näitena vaatleme viieelemendilist massiivi 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äisarvumassiivi puhul täisarvu, reaalarvumassiivi puhul reaalarvu jne, ning igale elemendile võib väärtuse omistada teistest elementidest sõltumatult. Javas kehtib reegel, et massiivi elementide numeratsioon algab alati nullist. Kui massiivis on kokku n
elementi, siis viimase elemendi indeks on n-1
. Niisugust ühekohalist „nihet“ võrreldes harjunud loendamisviisiga tuleb Java-programmide koostamisel silmas pidada. (Massiivi elemendid võivad olla ka ise massiivid (kõik siis muidugi üht tüüpi massiivid, küll aga võib nende pikkus olla erinev). See lause on lugemiskontrolliks - palun kirjutage paberile/dokumenti ennustus, mitu erakonda saame riigikokku. Sellisel juhul kujutatakse seda Javas kahemõõtmelise massiivina ja konkreetsele elemendile viidatakse kahe indeksiga, nt. b[2][3]
.)
Massiivi 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 massiivi
int[] a = {10, 9, 12, 11, 8};
Niisuguse kirjelduse toimel võtab arvuti kasutusele täisarvumassiivi 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 massiivi elementide arv ehk massiivi pikkus, mis saab konstandi a.length
väärtuseks (praegusel juhul on selleks 5).
Teine võimalus massiivi 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 massiivi. 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;
Massiivi ühe elemendi väärtust võib väljastada standardsel viisil nagu iga teist väärtust. Näiteks korraldus
System.out.println("Jarjendi esimene element on " + a[0]);
väljastab ekraanile massiivi a
(tavamõttes) esimese elemendi väärtuse.
Paneme tähele, et massiivi suuruse peab määrama massiivi loomise hetkel. Hiljem ei saa enam massiivi suurust muuta. See on erinev näiteks Pythoni listist, kuhu saab elemente nii palju sisse lisada, kui vaja on. Tegelikult on ka Javas olemas muutuva suurusega listid (klass ArrayList). Sellest räägitakse lähemalt juba 4. praktikumis.
Massiiv ja tsükkel
Massiivi kõigi väärtuste väljastamiseks tuleb terve massiiv, 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 massiivi pikkus. Indeksi nihke tõttu vaadatakse läbi parajasti kõik massiivi elemendid esimesest viimaseni. Näiteks 5-elemendilise massiivi 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 massiivis 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 massiivist üles vastava järjenumbriga elemendi.
Kui on vaja massiiv 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 (kontroll) --> P5 (kontroll)
Veepargi erinevatele atraktsioonidele lastakse lapsi vastavalt kasvule. Kõige väiksemad (60-100 cm) lapsed saavad minna väikelaste atraktsioonidele. Keskmised lapsed (101-140 cm) saavad minna lasteatraktsioonidele. Suuremad lapsed (141-200 cm) saavad minna täiskasvanute atraktsioonidele.
- Koostage meetod, mille argumentideks oleksid pikkuse alumine ja ülemine piir ja mis tagastaks juhuslikult genereeritud täisarvu antud vahemikust. Kasutage juhuarvu genereerimisel ja ümardamisel klassi
Math
vastavaid meetodeid. (Tegelikult on olemas ka klassRandom
, aga ärgem seda veel puutugem, kuna seal pole staatilisi meetodeid.) - Koostage meetod, mille argumentideks oleksid laste arv, pikkuste alumine ja ülemine piir. Looge meetodis massiiviloome abil täisarvude massiiv, mis sisaldab laste pikkusi, mis on juhuslikult genereeritud kasutades eelmist meetodit. Meetodi tagastustüüp peaks olema
int[]
. - Viimast meetodit peameetodis rakendades luua 10-liikmeline massiiv väiksemate laste jaoks, 15-liikmeline massiiv keskmiste laste jaoks ja 20-liikmeline massiiv suuremate laste jaoks.
- Väljastage peameetodis eraldi tsüklite abil ekraanile iga massiivi kõik elemendid. Seejärel väljastada tsükli ja valikulause (
if
) abil ainult need esimese massiivi elemendid, mille väärtus on üle 80. Katsetage mõlematfor
-tsükli versiooni. - Koostage meetod, mis leiab laste pikkuste harmoonilise keskmise. Formaalseks parameetriks on siis täisarvude massiiv. Rakendage seda meetodit. (Harmooniline keskmine on aritmeetilisest ja geomeetrilisest keskmisest vähem tuntud, aga mitmetel juhtudel siiski väga kasulik (näiteks keskmise kiiruse arvutamisel, kui võrdne teepikkus läbitakse erinevate ühtlaste kiirustega). Käesolevas pikkuste näites on see küll pigem kunstlik.)
Paberile/dokumenti kirjutada harmoonilise keskmise definitsioon ja koht, kust te selle leidsite (kui te seda peast ei teadnud).
Ülesanne P6 (kontroll) <-->A6
Palun kirjutage paberile/dokumenti 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õnemassiiv
Kui vaadata peameetodit, siis selle signatuuris paistab ka massiiv - sõnemassiiv. 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õnemassiivi, 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 (ingl.k wrapper class) 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 argumentideks reaalarv 1.67 ja täisarv 5 (java Matkajapikkus 1.67 5
), siis
double pikkus = Double.parseDouble(args[0]); int matkapäevi = Integer.parseInt(args[1]);
muudab esimese argumendi reaalarvuks ja teise täisarvuks, millega saab edaspidi tehteid teha.
Juhend käsurea parameetrite määramiseks IDEs: Eclipse ja IntelliJ jaoks
Kui on vaja luua sõnemasiivi programmi sees, siis toimub nii massiivi loomine kui ka tema elementidele väärtuste omistamine samamoodi kui arvumassiivide korral:
String[] sõned = {"Ilus", "punane", "maasikas"};
või
String[] sõned = new String[pikkus];
Ülesanne A7 (kontroll) --> P7 (kontroll)
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/dokumenti, millised on käsurealt lugemise ja eelmises praktikumis käsitletud klaviatuurilt sisestamise põhilised erinevused. Milliste programmide puhul võiks neid rakendada?
Kuidas õppida uut programmeerimiskeelt?
Programmeerimise õppimisel võib tekkida mõte, et keele süntaksit pole mõtet pähe õppida, kuna tänapäeval on alati käepärast Google või siis mõned enda poolt varem kirjutatud programmid. Analoogselt võiksime ka öelda, et võõrkeelse teksti lugemiseks pole vaja sõnu peast teada, sest sõnaraamat või Google Translate on alati käepärast. Mõlemal juhul tuleb siiski arvestada, et baasmaterjali päheõppimine teeb edasise tööprotsessi palju efektiivsemaks. Võib-olla mäletate isegi seda, kui alustasite mingi võõrkeele õppimist ja pidite teksti lugemisel iga teist sõna sõnastikust järgi vaatama, mõnikord läks lause algus enne meelest, kui lause lõppu jõudsid.
Ka Java õppimisel on kasulik õppida baassüntaksi võimalikult varakult pähe. Selleks on soovitav mõned korrad võtta ette tühi fail ja proovida üks lihtne Java programm kirja panna ilma ühegi copy-pasteta. Sellise lähenemise kasulikkust on põhjendanud Zed A. Shaw oma populaarsete programmeerimisõpikute eessõnades (http://learncodethehardway.org/). Tema õpikute stiili peavad paljud liiga radikaalseks, kuid iva tema jutus kindlasti on.
Seni, kuni Java põhiasjad veel veatult meeles pole, proovige hoida vastav info käepärast, see ongi see raudvara, mida ülalpool soovitati. Väga palju on abi ühest A4 paberist, kui ise kirjutada või trükkida mõned süntaksinäited. Paber on hea seetõttu, et sellelt lugemiseks ei pea ekraanil aknaid vahetama ning see sunnib koodi oma käega ümber kirjutama ja sel teel saab süntaks palju kiiremini selgeks.
Enne teist praktikumi!
- Teise praktikumi kontrollülesanneteks on arvutiülesanded A4, A5, A7 (need tuleb laadida Moodle'isse) ja kõik paberi (P-tähega) ülesanded (neid võite tuua paberil või laadida siis tehtud dokument Moodle'isse).
- Kontrollige, kas Te olete kõik selle ja eelmise praktikumi ülesanded ära teinud. Kui pole, siis tehke.
- Kui midagi selgusetuks jäi, siis palun küsige järgmisel praktikumil praktikumijuhendajalt või kirjutage Moodle'is foorumisse.
- Kas oskate mõnele foorumis olevale vastuseta küsimusele vastata? Vastake.
- Vaadake Moodle-is olevat
Pythoni ja Java võrdlust
.