(Muutettu viimeksi 1.10.2011, sivu perustettu 19.9.2011.)
Nämä harjoitukset liittyvät oppimateriaalin lukuihin 2 Oliot ja kapselointi ja 3 Ohjelmointitekniikkaa: standardisyöttövirta ja Scanner
Kaikki harjoitustehtävät on syytä tehdä. Jotkin tehtävät on merkitty keltaisella värillä. Ne ovat ehkä hieman muita haastavampia. Ilman niitäkin harjoituksista voi saada maksimipisteet, mutta ne lasketaan silti mukaan harjoituspisteitä määrättäessä – ne voivat siis korvata joitakin haasteettomampia tehtäviä tms. Mutta ennen kaikkea noista keltaisista tehtävistä sitä vasta oppiikin!
Huom:
// 5. harjoitukset, tehtävä 1.2, Oili Opiskelija
Tässä tehtäväsäsarjassa tehdään luokka LyyraKortti
,
jonka tarkoituksena on jäljitellä Lyyra-kortin käyttämistä Unicafessa.
Projektiin tulee kuulumaan kaksi kooditiedostoa:
Main.java
: pääohjelmaLyyraKortti.java
: luokka LyyraKortti
Kun luot projektin NetBeansissa,
anna projektin nimeksi (Project Name) LyyraKortti
ja pääluokan nimeksi (Main Class) Main
.
Lisää sitten projektiin uusi luokka painamalla
projektin nimestä hiiren oikealla napilla
vasemmalla olevasta projektilistasta ja
valitsemalla New->Java Class.
Anna luokan nimeksi (Class Name) LyyraKortti
.
Tee ensin LyyraKortti
-olion konstruktori,
jolle annetaan kortin alkusaldo ja
joka tallentaa sen olion sisäiseen muuttujaan.
Tee sitten toString
-metodi,
joka palauttaa kortin saldon muodossa "Kortilla on rahaa X euroa".
Seuraavassa on luokan LyyraKortti
runko:
public class LyyraKortti { private double saldo; public LyyraKortti(double alkusaldo) { // kirjoita koodia tähän } public String toString() { // kirjoita koodia tähän } }
Seuraava pääohjelma testaa luokkaa:
public class Main { public static void main(String[] args) { LyyraKortti kortti = new LyyraKortti(50); System.out.println(kortti); } }
Ohjelman tulisi tuottaa seuraava tulostus:
Kortilla on rahaa 50.0 euroa
Täydennä LyyraKortti
-luokkaa seuraavilla metodeilla:
public void syoEdullisesti() { // kirjoita koodia tähän } public void syoMaukkaasti() { // kirjoita koodia tähän }
Metodin syoEdullisesti
tulisi vähentää kortin saldoa 2,40 eurolla ja
metodin syoMaukkaasti
tulisi vähentää kortin saldoa 4,00 eurolla.
Seuraava pääohjelma testaa luokkaa:
public class Main { public static void main(String[] args) { LyyraKortti kortti = new LyyraKortti(50); System.out.println(kortti); kortti.syoEdullisesti(); System.out.println(kortti); kortti.syoMaukkaasti(); kortti.syoEdullisesti(); System.out.println(kortti); } }
Ohjelman tulisi tuottaa seuraava tulostus:
Kortilla on rahaa 50.0 euroa Kortilla on rahaa 47.6 euroa Kortilla on rahaa 41.2 euroa
Mitä tapahtuu, jos kortilta loppuu raha kesken?
Ei ole järkevää, että saldo muuttuu negatiiviseksi.
Muuta metodeita syoEdullisesti
ja syoMaukkaasti
niin,
että ne eivät vähennä saldoa, jos saldo menisi negatiiviseksi.
Seuraava pääohjelma testaa luokkaa:
public class Main { public static void main(String[] args) { LyyraKortti kortti = new LyyraKortti(5); System.out.println(kortti); kortti.syoMaukkaasti(); System.out.println(kortti); kortti.syoMaukkaasti(); System.out.println(kortti); } }
Ohjelman tulisi tuottaa seuraava tulostus:
Kortilla on rahaa 5.0 euroa Kortilla on rahaa 1.0 euroa Kortilla on rahaa 1.0 euroa
Siis toinen metodin syoMaukkaasti
kutsu ei
vaikuttanut saldoon, koska saldo olisi mennyt negatiiviseksi.
Lisää LyyraKortti
-luokkaan seuraava metodi:
public void lataaRahaa(double rahamaara) { // kirjoita koodia tähän }
Metodin tarkoituksena on kasvattaa kortin saldoa parametrina annetulla rahamäärällä. Kuitenkin kortin saldo saa olla korkeintaan 150 euroa, joten jos ladattava rahamäärä ylittäisi sen, saldoksi tulisi tulla silti tasan 150 euroa.
Seuraava pääohjelma testaa luokkaa:
public class Main { public static void main(String[] args) { LyyraKortti kortti = new LyyraKortti(10); System.out.println(kortti); kortti.lataaRahaa(15); System.out.println(kortti); kortti.lataaRahaa(10); System.out.println(kortti); kortti.lataaRahaa(200); System.out.println(kortti); } }
Ohjelman tulisi tuottaa seuraava tulostus:
Kortilla on rahaa 10.0 euroa Kortilla on rahaa 25.0 euroa Kortilla on rahaa 35.0 euroa Kortilla on rahaa 150.0 euroa
Tee pääohjelma, jossa luodaan kaksi LyyraKortti
-oliota:
yksi Pekalle (alkusaldo 20 euroa)
ja toinen Matille (alkusaldo 30 euroa).
Tee ohjelmassa seuraavat asiat:
Pääohjelman runko on seuraava:
public class Main { public static void main(String[] args) { LyyraKortti pekanKortti = new LyyraKortti(20); LyyraKortti matinKortti = new LyyraKortti(30); // kirjoita koodia tähän } }
Ohjelman tulisi tuottaa seuraava tulostus:
Pekka: Kortilla on rahaa 16.0 euroa Matti: Kortilla on rahaa 27.6 euroa Pekka: Kortilla on rahaa 36.0 euroa Matti: Kortilla on rahaa 23.6 euroa Pekka: Kortilla on rahaa 31.200000000000003 euroa Matti: Kortilla on rahaa 73.6 euroa
Tosielämässä kortin saldon tallennus
double
-muuttujaan ei olisi hyvä idea,
koska double
-arvoissa esiintyy
pyöristysvirheitä.
Sellainen esiintyy jopa yllä olevassa esimerkissä:
Pekan kortilla kuuluisi olla lopuksi 31,2 euroa,
mutta siellä onkin muka 31,200000000000003 euroa.
Yksi ratkaisu ongelmaan on käyttää
double
-muuttujan sijasta
int
-muuttujaa ja tallentaa
rahamäärä sentteinä.
Tee tarvittavat muutokset luokkaan.
Täydennetään edellisen tehtäväsarjan LyyraKortti-luokkaa.
Lisää luokkaan LyyraKortti
uusi konstruktori,
jolla ei ole parametreja. Tässä tapauksessa
kortin aloitussaldon tulee olla 20 euroa.
Seuraava pääohjelma testaa luokkaa:
public class Main { public static void main(String[] args) { LyyraKortti kortti1 = new LyyraKortti(); System.out.println(kortti1); LyyraKortti kortti2 = new LyyraKortti(30); System.out.println(kortti2); } }
Ohjelman tulisi tuottaa seuraava tulostus:
Kortilla on rahaa 20.0 euroa Kortilla on rahaa 30.0 euroa
Metodien syoEdullisesti
ja syoMaukkaasti
ongelmana on, että niissä on samantapainen tarkistus sen varalta,
että kortin saldo uhkaa mennä negatiiviseksi.
On selkeämpää ja turvallisempaa, että tarkistus ohjelmoidaan
vain yhteen paikkaan.
Tee apumetodi vahennaSaldoa
,
jonka tehtävänä on vähentää kortin saldoa annetulla rahamäärällä.
Metodin runko on seuraava:
private void vahennaSaldoa(double rahamaara) { // kirjoita koodia tähän }
Tee tämäkin metodi niin, että metodi ei muuta saldoa, jos saldo menisi negatiiviseksi.
Toteuta nyt metodit syoEdullisesti
ja
syoMaukkaasti
niin, että ne kutsuvat metodia
vahennaSaldoa
. Nyt negatiivisen saldon
tarkistus on vain yhdessä kohdassa.
(Näin mahdollinen muuttaminen ja korjaaminen on helpompaa ja
turvallisempaa!)
Muista myös testata, että metodit toimivat tämän muutoksen jälkeen.
Rakennellaan ensin välineitä ja sitten sovellus. NetBeansin käyttäjän lienee järkevää ohjelmoida tämän kohdan tehtävät yhteen ja samaan projektiin.
NumeronArpoja-olio on kone, joka palvelee tuottamalla satunnaisen luvun väliltä 1 — annettu yläraja. Luokan yksinkertanen API on
ylaraja
:
Ohjelmoi luokkaan myös yksinkertainen pääohjelma, joka esittelee NumeronArpoja-olioiden käyttöä.
Vihje: Satunnaisluvun väliltä 1 — ylaraja
saa arvottua seuraavasti:
int arvottuLuku = (int)(ylaraja*Math.random()) + 1;
NumeronArpoja-konetta siis käytetaan seuraavaan tapaan:
// ... NumeronArpoja alleSatanen = new NumeronArpoja(100); int luku = alleSatanen.getSeuraavaLuku(); // jokin luvuista 1, 2, ..., 100 // ... NumeronArpoja vuosiluku = new NumeronArpoja(2011); int vuosi = vuosiluku.getSeuraavaLuku(); // jokin luvuista 1, 2, ..., 2011 // ... NumeronArpoja noppa = new NumeronArpoja(6); System.out.println("10 nopanheittoa:"); for (int i = 0; i < 10; i++) { System.out.println(noppa.getSeuraavaLuku()); }
Ohjelmoi "kone" RajoitettuIntLukija, jolla voi pyytää käyttäjältä kokonaislukua annetulta kokonaislukuväliltä. Luokan ilmentymä eli RajoitettuIntLukija-olio kyselee ohjelman käyttäjältä ponnahdusikkunan avulla oikean kokoista lukua niin pitkään, että käyttäjä ymmärtää antaa kelvollisen.
RajoitettuIntLukija-luokan API on seuraavanlainen:
alaraja
— ylaraja
.
alaraja
— ylaraja
.
Aksessorin pitää virheellisen syötteen tapauksessa antaa
virheilmoitus ja pyytää sisukkaasti uutta lukua kunnes saa kelvollisen.
Miten voisit varautua virheeseen, jossa yritetään luoda epäkelpo RajoitettuIntLukija-olio? Esimerkiksi jos yritetään asettaa: alaraja=3 ja ylaraja=1. Toistaiseksi voisit ehkä tehdä "rikkinäisen" olion, joka palauttaa aina alarajan. Tämä ei ole hyvä ratkaisu, mutta riittäköön toistaiseksi. Asiaan palataan vielä...
RajoitettuIntLukija-olioita voisi ohjelmoinnissa käyttää seuraavaan tapaan:
// ... RajoitettuIntLukija neljastaKymppiin = new RajoitettuIntLukija(4, 10); RajoitettuIntLukija itsArvoTonni = new RajoitettuIntLukija(-1000, 1000); // ... Pop.ilmoita("Anna kouluarvosana!"); int arvosana = neljastaKymppiin.lueInt(); // nyt arvosana on kokonaisluku väliltä 4-10 Pop.ilmoita("Arvosana on " + arvosana); // ... Pop.ilmoita("Anna luku, jolle |luku| <= 1000!"); int luku = itsArvoTonni.lueInt(); // nyt luku on kokonaisluku väliltä -1000 - 1000 Pop.ilmoita("Luku on " + luku); // ...
Ja tuon ohjelmakohdan suoritus voisi puolestaan näyttää seuraavalta. Huomaa, miten virheestä ei ilmoiteta, ellei virhettä tule.
Tämä tehtävä tehdään käyttäen edellisissä tehtävissä ohjelmoituja välineitä NumeronArpoja ja RajoitettuIntLukija!
Toteuta seuraava tietokonepeli: Ensin ohjelma arpoo jonkin kokonaisluvun 1, 2, ..., 10 paljastamatta sitä ohjelman käyttäjälle. Sitten käyttäjä syöttää ohjelmalle kolme arvausta siitä, minkä luvun ohjelma on arponut.
Lopuksi ohjelma selvittää ja ilmoittaa pelin tuloksen:
Mikään ei estä kayttäjää syöttämästä eli arvaamasta samaa lukua useampaankin kertaan; tällöin sekä riski hävitä että mahdollinen voitto kasvavat.
Tehtävän ratkaisuksi riittää toteuttaa yhden luvun arvaaminen, ts. yksi pelikerta.
Laajenna edellisen tehtävän uhkapeliohjelmaa siten, että pelaaja voi pelata haluamansa määrän kierroksia. Ohjelma pitää kirjaa pelaajan voitoista ja tappioista. Päätä itse, miten ohjelma keskustelee käyttäjän kanssa, miten pelaaminen päättyy ja millaisia raportteja tulostetaan.
Jos haluat, voit mallintaa myös pelaajan hallussa olevan alkupääoman ja seurata pääoman muutoksia. Pelaajalla on siis lompakko tai tili, jota voitot ja tappiot päivittävät. Ja jos tili tyhjenee, pelaaminen loppuu, koska tässä kasinossa ei suvaita velaksi pelaamista.
Kurssilla on tähän saakka tulostettu tietoja ponnahdusikkuinoin ja myös kirjoitettu standarditulosvirtaan. Kaikki tietojen lukeminen on toteutettu ponnahdusikkunoin. Nyt on aika harjoitella "klassista tapaa" ohjelman syötteiden ja tulosteiden käsittelyssä. Tässä tehtäväsarjassa ei saa käyttää ponnahdusikkunoita. Tässä tehtäväsarjassa ei tarvitse varautua virheellisiin syötteisiin. Kokeile silti, miten käy...
Palataan hetkeksi uudelleen alun tunnelmiin ja silloisiin pikku tehtäviin:
(Vrt. 1. viikon tehtävä 3.1)
Ohjelma pyytää kaksi kokonaislukua ja ilmoittaa niiden summan.
Esimerkkisuoritus (käyttäjän kirjoittama teksti on kursiivilla):
Anna ensimmäinen luku! 6 Anna toinen luku! 8 Niiden summa on 14
(Vrt. 1. viikon tehtävä 3.5)
Tee ohjelma, joka kysyy käyttäjältä nimen iän. Sitten ohjelma tervehtii käyttäjää ja kertoo samalla tämän iän.
Esimerkkisuoritus: (käyttäjän kirjoittama teksti on kursiivilla):
Mikä on nimesi? Matti Mikä on ikäsi 14 Hei Matti, olet siis 14!
(Vrt. 2. viikon tehtävä 4.4)
Negatiivisten ja positiivisten kokonaislukujen määrä: Ohjelma pyytää käyttäjältä kokonaislukuja. Luku nolla ilmaisee, että syöttöluvut loppuivat. Ohjelman tehtävänä on laskea syötteen positiivisten ja negatiivisten lukujen lukumäärä.
Esimerkkisuoritus: (käyttäjän kirjoittama teksti on kursiivilla):
Positiivisten ja negatiivisten laskenta *************************************** Anna lukuja! Nolla lopettaa! 4 -2 -33 9 123 31 7 0 Positiivisia: 5 Negatiivisia: 2
(Vrt. 3. viikon tehtävä 6.5)
Opettaja syöttää opiskelijoiden koepistemäärät. Kukin syötetty koepistemäärä muutetaan vastaavaksi arvosanaksi. Jokainen laskettu arvosana kasvattaa yhdellä arvosanojen lukumääriä laskevan taulukon oikeaa alkiota. Negatiivinen syöttöluku lopettaa pisteiden syötön.
Esimerkkisuoritus: (käyttäjän kirjoittama teksti on kursiivilla):
Arvosanajakauman laskenta ========================= Anna pistemäärät! (Negatiivinen syöte lopettaa.) 44 35 17 56 7 54 -1 Arvosanajakauma: 5: ** 4: 3: * 2: * 1: 0: ** Hyväksyttyjä 66.66666667 % osallistujista
Tässä tehtäväsarjassa mallinnetaan liioitellun yksinkertaisesti kurssikirjanpitoa. Harjoittelun ytimessä on luokka, joka sisältää taulukollisen toisen luokan ilmentymiä.
Ohjelmoi luokka Opiskelija. API:
Käyttöesimerkki:
Opiskelija maija = new Opiskelija("Maija"); maija.setPisteet(54); System.out.println(maija.getNimi()); // Maija System.out.println(maija.getPisteet()); // 54 System.out.println(maija); // Maija: 54
Kurssikirjapitoa varten tarvitaan luokka, jonka ilmentymä sisältää taulukollisen opiskelijoita. Ohjelmoi tähän tarkoitukseen luokka Kurssi, jonka API on:
Liisa: 46 Maija: 54 Pekka: 44 Antti: 8 Evita: 16 Barbara: 37
Luokkamäärittelyn hahmottelua:
public class Kurssi { private Opiskelija[] opiskelijat; public Kurssi(int opiskelijaLkm) { opiskelijat = // ... oikean kokoinen Opiskelija-taulukko } public void kysyOpiskelijat() { // käy läpi opiskelijat-talukko: 0 - taulukon viimeinen alkio { // kysele opiskelijan tiedot // luo Opiskelija-olio ja sijoita se taulukkoon // } } public boolean asetaPisteet(String nimi, int pisteet) { // käy läpi opiskelijat-talukkoa kunnes (jos) löytyy // Opiskelija-olio, jolle opiskelijat[i].getNimi().equals(nimi) // jos löytyi, aseta opiskelijat[i].setPisteet(pisteet) // palauta arvo true tai false riippuen siitä, löytyikö opiskelija } public String toString() { // String tulos = ""; // käy läpi opiskelijat-talukko: 0 - taulukon viimeinen alkio // tulos = tulos + opiskelijat[i] + "\n"; // palauta tulos } }
Käyttöesimerkki
Kurssi ohpe = new Kurssi(6); ohpe.kysyOpiskelijat(); // syötetään Liisa, Maija, Pekka, Antti, Evita, Barbara if (ohpe.asetaPisteet("Maija", 54)) System.out.println("Maijan pisteiden asetus onnistui."); else System.out.println("Maijan pisteiden asetus ei onnistunut."); if (ohpe.asetaPisteet("Petteri", 4)) System.out.println("Petterin pisteiden asetus onnistui."); else System.out.println("Petterin pisteiden asetus ei onnistunut."); System.out.println(ohpe);
Tulostuksen pitäisi näyttää seuraavalta:
Maijan pisteiden asetus onnistui. Petterin pisteiden asetus ei onnistunut. Liisa: 0 Maija: 54 Pekka: 0 Antti: 0 Evita: 0 Barbara: 0
Laadi luokkia Opiskelija ja Kurssi käyttäen vuorovaikutteinen (eli keskusteleva eli interaktiivinen) yksinkertainen sovellus kurssikirjanpitoon. Ensin ohjelma kyselee opiskelijoiden lukumäärän, sitten kaikkien nimet. Kun ohjelma on saanut kurssin muodostettua, se tarjoaa pistemäärien päivityspalvelun:
Annetaan nimi. Jos nimi on tyhjä merkkijono ("") päivitykset päättyvät. Jos nimi ei ollut tyhjä, kysytään nimeen liitettävät pisteet. Tätä jatketaan siis tyhjään merkkijonoon saakka. Virheellistä pistemäärää ei hyväksytä. Sovellus antaa sellaisesta selkeän virheilmoituksen ja tarjoaa korjaamismahdollisuuden.
Lopuksi ohjelma tulostaa kurssin tulokset muodossa, jonka Kurssi-luokan toString-metodi tarjoaa.