Tehtävät viikolle 6

Pakolliset tehtävät on merkitty harmalla taustavärillä. Punaisella taustavärillä merkatut ovat kontrollitehtäviä, jotka näytetään ohjaajalle harjoitustilaisuudessa!

Muutama NetBeans-vihje

Lyyra-kortti ja Kassapääte

"Tyhmä" Lyyra-kortti

Aloitimme viime viikolla olioiden ohjelmoinnin tekemällä LyyraKortti-olioita. Kortilla oli metodit edullisesti ja maukkaasti syömistä sekä rahan lataamista varten.

Viime viikon tyylillä tehdyssä Lyyra-kortissa oli kuitenkin ongelma. Kortti tiesi lounaiden hinnan ja osasi sen ansiosta vähentää saldoa oikean määrän. Entä kun hinnat nousevat? Tai jos myyntivalikoimaan tulee uusia tuotteita? Hintojen muuttaminen tarkoittaisi, että kaikki jo käytössä olevat Lyyra-kortit pitäisi korvata uusilla, uudet hinnat tuntevilla korteilla.

Parempi ratkaisu on tehdä kortit "tyhmiksi", hinnoista ja myytävistä tuotteista tietämättömiksi pelkän saldon säilyttäjiksi. Kaikki äly kannattaakin laittaa erillisiin olioihin, kassapäätteisiin.

Toteutetaan ensin Lyyra-kortista "tyhmä" versio. Kortilla on ainoastaan metodit saldon kysymiseen, rahan lataamiseen ja rahan ottamiseen. Täydennä allaoleva luokka ohjeen mukaan:

public class LyyraKortti {
    private double saldo;

    public LyyraKortti(double saldo) {
        this.saldo = saldo;
    }

    public double saldo() {
        return saldo;
    }

    public void lataaRahaa(double lisays) {
        this.saldo += lisays;
    }

    public boolean otaRahaa(double maara){
       // toteuta metodi siten että se ottaa kortilta rahaa vain jos saldo on vähintään maara
       // onnistuessaan metodi palauttaa true ja muuten false
    }
}

Testipääohjelma:

public class Main {
    public static void main(String[] args) {
        LyyraKortti pekanKortti = new LyyraKortti(10);

        System.out.println("rahaa " + pekanKortti.saldo() );
        boolean onnistuiko = pekanKortti.otaRahaa(8);
        System.out.println("onnistuiko otto: " + onnistuiko );
        System.out.println("rahaa " + pekanKortti.saldo() );

        onnistuiko = pekanKortti.otaRahaa(4);
        System.out.println("onnistuiko otto: " + onnistuiko );
        System.out.println("rahaa " + pekanKortti.saldo() );
    }
}

Tulostuksen kuuluisi olla seuraavanlainen

rahaa 10.0
onnistuiko otto: true
rahaa 2.0
onnistuiko otto: false
rahaa 2.0

Kassapääte ja käteiskauppa

Unicafessa asioidessa asiakas maksaa joko käteisellä tai Lyyra-kortilla. Myyjä käyttää kassapäätettä kortin velottamiseen ja käteismaksujen hoitamiseen. Tehdään ensin kassapäätteestä käteismaksuihin sopiva versio.

Kassapäätteen runko:

public class Kassapaate {
    private double rahaa;  // kassassa olevan käteisen määrä
    private int edulliset;
    private int maukkaat;

    public Kassapaate() {
	// kassassa on aluksi 1000 euroa rahaa
    }

    public double syoEdullisesti(double maksu) {
        // edullinen lounas maksaa 2.40 euroa. 
        // päivitetään kassan rahamäärää ja palautetaan vaihtorahat
    }

    public double syoMaukkaasti(double maksu) {
        // edullinen lounas maksaa 4.00 euroa. 
        // päivitetään kassan rahamäärää ja palautetaan vaihtorahat
    }

    public String toString() { 
        // palautetaan merkkijonona kassan rahamäärä sekä tieto myydyistä lounaista
    }
}

Kassapäätteessä on aluksi rahaa 1000 euroa. Toteuta ylläolevan rungon metodit ohjeen ja allaolevan pääohjelman esimerkkitulosteen mukaan toimiviksi.

public class Main {
    public static void main(String[] args) {
        Kassapaate unicafeExactum = new Kassapaate();
        
        double vaihtorahaa = unicafeExactum.syoEdullisesti(10);
        System.out.println("vaihtorahaa jäi " + vaihtorahaa );

        vaihtorahaa = unicafeExactum.syoEdullisesti(5);
        System.out.println("vaihtorahaa jäi "  + vaihtorahaa );

        vaihtorahaa = unicafeExactum.syoMaukkaasti(4);
        System.out.println("vaihtorahaa jäi "  + vaihtorahaa );

        System.out.println( unicafeExactum );
   }
}
vaihtorahaa jäi 7.6
vaihtorahaa jäi 2.6
vaihtorahaa jäi 0.0
kassassa rahaa 1008.8 edullisia lounaita myyty 2 maukkaita lounaita myyty 1

Kortilla maksaminen

Laajennetaan kassapäätettä siten että myös kortilla voi maksaa. Teemme kassapäätteelle siis metodit joiden parametrina kassapääte saa lyyrakortin jolta se vähentää valitun lounaan hinnan. Seuraavassa uusien metodien rungot ja ohje niiden toteuttamiseksi:

public class Kassapaate {    
    // ...

    public boolean syoEdullisesti(LyyraKortti kortti) {
        // edullinen lounas maksaa 2.40 euroa. 
        // jos kortilla on tarpeeksi rahaa, vähennetään hinta kortilta ja palautetaan true
        // muuten palautetaan false
    }

    public boolean syoMaukkaasti(LyyraKortti kortti) {
        // edullinen lounas maksaa 4.00 euroa.
        // jos kortilla on tarpeeksi rahaa, vähennetään hinta kortilta ja palautetaan true
        // muuten palautetaan false
    }

    // ...
}

Huom: kortilla maksaminen ei lisää kassapäätteessä olevan käteisen määrää.

Seuraavassa testipääohjelma ja haluttu tulostus:

public class Main {
    public static void main(String[] args) {
        Kassapaate unicafeExactum = new Kassapaate();

        double vaihtorahaa = unicafeExactum.syoEdullisesti(10);
        System.out.println("vaihtorahaa jäi " + vaihtorahaa );

        LyyraKortti antinKortti = new LyyraKortti(7);      

        boolean onnistuiko = unicafeExactum.syoMaukkaasti(antinKortti);
        System.out.println("riittikö raha: " + onnistuiko);
        onnistuiko = unicafeExactum.syoMaukkaasti(antinKortti);
        System.out.println("riittikö raha: " + onnistuiko);
        onnistuiko = unicafeExactum.syoEdullisesti(antinKortti);
        System.out.println("riittikö raha: " + onnistuiko);

        System.out.println( unicafeExactum );
    }
}
vaihtorahaa jäi 7.6
riittikö raha: true
riittikö raha: false
riittikö raha: true
kassassa rahaa 1002.4 edullisia lounaita myyty 2 maukkaita lounaita myyty 1

Copypaste pois

Viime viikon tehtävässä 3.2 kehoitettiin poistamaan Lyyra-kortin kahdesta eri metodista samantapainen koodi uuden metodin avulla. On hyvin todennäköistä, että Kassapaate-luokassasi on nyt "copypaste"-koodia. Poista se viime viikon tehtävän 3.2. ohjeita soveltaen.

On todennäköistä, että lounaiden hinnat muuttuvat tulevaisuudessa. Tee koodistasi sellainen, että lounaan hinnat 2.4 euroa ja 4.0 euroa eivät esiinny kassapäätteen koodissa kuin korkeintaan yhdessä kohdassa. Mieti vielä että tapa miten hinnat kassapäätteessä esiintyvät tekee niiden muuttamisen mahdollisimman helpoksi.

Rahan lataaminen

Lisätään vielä kassapäätteelle metodi jonka avulla kortille voidaan ladata lisää rahaa. Muista, että rahan lataamisen yhteydessä ladattava summa viedään kassapäätteeseen. Metodin runko:

    public void lataaRahaaKortille(LyyraKortti kortti, double summa) {
       // ...
    }   

Testipääohjelma ja esimerkkisyöte:

public class Main {
    public static void main(String[] args) {
        Kassapaate unicafeExactum = new Kassapaate();
        System.out.println( unicafeExactum );

        LyyraKortti antinKortti = new LyyraKortti(2);

        System.out.println("kortilla rahaa " + antinKortti.saldo() + " euroa");

        boolean onnistuiko = unicafeExactum.syoMaukkaasti(antinKortti);
        System.out.println("riittikö raha: " + onnistuiko);

        unicafeExactum.lataaRahaaKortille(antinKortti, 100);

        onnistuiko = unicafeExactum.syoMaukkaasti(antinKortti);
        System.out.println("riittikö raha: " + onnistuiko);

        System.out.println("kortilla rahaa " + antinKortti.saldo() + " euroa");

        System.out.println( unicafeExactum );
    }
}
kassassa rahaa 1000.0 edullisia lounaita myyty 0 maukkaita lounaita myyty 0
kortilla rahaa 2.0 euroa
riittikö raha: false
riittikö raha: true
kortilla rahaa 98.0 euroa
kassassa rahaa 1100.0 edullisia lounaita myyty 0 maukkaita lounaita myyty 1

Päivämäärien erotus

Kahden päiväyksen erotus vuosissa

Materiaalissa esiteltiin luokka päiväyksen esittämiseen:

public class Paivays {
    private int pv;
    private int kk;
    private int vv;

    public Paivays(int pv, int kk, int vv) {
        this.pv = pv;
        this.kk = kk;
        this.vv = vv;
    }

    public String toString() {
        return pv+"."+kk+"."+vv;
    }

    public boolean aiemmin(Paivays verrattava){
        if ( this.vv < verrattava.vv )
            return true;

        if ( this.vv == verrattava.vv && this.kk < verrattava.kk )
            return true;

        if ( this.vv == verrattava.vv && this.kk == verrattava.kk &&
                this.pv < verrattava.pv )
            return true;

        return false;
    }
}

Lisää päiväykselle metodi public int erotusVuosissa(Paivays verrattava), jonka avulla saadaan selville kahden päivämäärän erotus vuosissa.

Ensimmäisessä versiossa metodi toimii vasta aika karkealla tasolla, se ainoastaan laskee verrattavien päiväysten vuosilukujen erotuksen.

Huom: metodin tarvitsee toimia ainoastaan siten, että parametriksi annettava päivämäärä on aiempi kuin se päivämäärä jolle metodia kutsutaan.

Kokeile metodia esim. seuraavalla pääohjelmalla:

public class Main {
    public static void main(String[] args) {
        Paivays eka = new Paivays(24, 12, 2009);
        Paivays toka = new Paivays(1, 1, 2011);
        Paivays kolmas = new Paivays(25, 12, 2010);

        System.out.println( toka + " ja " + eka + " ero vuosissa: " + toka.erotusVuosissa(eka) );

        System.out.println( kolmas + " ja " + eka + " ero vuosissa: " + kolmas.erotusVuosissa(eka) );

        System.out.println( toka + " ja " + kolmas + " ero vuosissa: " + toka.erotusVuosissa(kolmas) );
    }
}

Tulos näyttää seuraavalta:

1.1.2011 ja 24.12.2009 ero vuosissa: 2
25.12.2010 ja 24.12.2009 ero vuosissa: 1
1.1.2011 ja 25.12.2010 ero vuosissa: 1

Tarkennettu versio

Vuosien laskenta ei edellisessä versiossa ollut vielä kovin tarkkaa. Esim. 1.1.2011 ja 25.12.2010 välillä ilmoitettiin olevan vuoden ero. Tarkennetaan metodin toiminta sellaiseksi, että se osaa laskea vuodet kunnolla.

Laske erotukseen mukaan vaan täydet vuodet. Eli vaikka päiväysten ero olisi 1 vuosi ja 364 päivää, ilmoittaa metodi eroksi vuoden.

1.1.2011 ja 24.12.2009 ero vuosissa: 1
25.12.2010 ja 24.12.2009 ero vuosissa: 1
1.1.2011 ja 25.12.2010 ero vuosissa: 0

Ja lopullinen versio

Laitetaan metodi toimimaan samoin riippumatta onko parametrina annettava päiväys myöhempi vai aiempi kuin päiväys mille metodia kutsutaan. Tulostuksen pitäisi olla seuraava:

24.12.2009 ja 1.1.2011 ero vuosissa: 1
1.1.2011 ja 24.12.2009 ero vuosissa: 1
24.12.2009 ja 25.12.2010 ero vuosissa: 1
25.12.2010 ja 24.12.2009 ero vuosissa: 1
1.1.2011 ja 25.12.2010 ero vuosissa: 0
25.12.2010 ja 1.1.2011 ero vuosissa: 0
public class Main {
    public static void main(String[] args) {
        Paivays eka = new Paivays(24, 12, 2009);
        Paivays toka = new Paivays(1, 1, 2011);
        Paivays kolmas = new Paivays(25, 12, 2010);

        System.out.println( eka + " ja " + toka + " ero vuosissa: " + toka.erotusVuosissa(eka) );
        System.out.println( toka + " ja " + eka + " ero vuosissa: " + eka.erotusVuosissa(toka) );
        System.out.println( eka + " ja " + kolmas + " ero vuosissa: " + kolmas.erotusVuosissa(eka) );
        System.out.println( kolmas + " ja " + eka + " ero vuosissa: " + eka.erotusVuosissa(kolmas) );
        System.out.println( kolmas + " ja " + toka + " ero vuosissa: " + toka.erotusVuosissa(kolmas) );
        System.out.println( toka + " ja " + kolmas + " ero vuosissa: " + kolmas.erotusVuosissa(toka) );
    }
}

Vihje: jos olet ovela ja hyödynnät metodia aiemmin, on tämä tehtävä erittäin helppo tehdä.

Opiskelija-luokka

Opiskelija-luokka

Tee luokka Opiskelija, johon tallennetaan seuraavat tiedot opiskelijasta:

Tee luokkaan seuraavat metodit:

Seuraava pääohjelma testaa luokan toimintaa:

public class Main {
    public static void main(String[] args) {
	Opiskelija pekka = new Opiskelija("Pekka Mikkola", "013141590");
        System.out.println("Nimi: " + pekka.haeNimi());
        System.out.println("Opiskelijanumero: " + pekka.haeOpiskelijanumero());
	System.out.println(pekka);
    }
}

Ohjelman tulostuksen tulisi olla seuraava:

Nimi: Pekka Mikkola
Opiskelijanumero: 013141590
Pekka Mikkola (013141590)

Opiskelijalista

Tee uusi pääohjelma, joka kysyy opiskelijoiden tietoja. Ohjelma luo jokaisesta opiskelijasta uuden olion ja tallentaa sen listaan. Kun käyttäjä antaa nimeksi tyhjän merkkijonon, ohjelma tulostaa listalla olevat opiskelijat.

Listan määrittelyn tulisi olla seuraava:

ArrayList<Opiskelija> lista = new ArrayList<Opiskelija>();

Seuraavassa on esimerkki ohjelman suorituksesta:

Nimi: Alfred Apina
Opiskelijanumero: 017635727
Nimi: Bruno Banaani
Opiskelijanumero: 011288989
Nimi: Cecilia Cembalo
Opiskelijanumero: 013672548
Nimi: 

Alfred Apina (017635727)
Bruno Banaani (011288989)
Cecilia Cembalo (013672548)

Opiskelijahaku

Tee ohjelma, jonka avulla voi hakea opiskelijat, joiden nimessä on annettu hakusana. Ohjelman alussa listaan lisätään joukko opiskelijoita. Tehtävänäsi on toteuttaa hakutoiminto.

Vihje: Käy opiskelijat läpi silmukassa ja tarkista String-luokan indexOf-metodilla, onko hakusana opiskelijan nimessä.

import java.util.*;
public class Main {
    private static Scanner lukija = new Scanner(System.in);
    public static void main(String[] args) {
	ArrayList<Opiskelija> lista = new ArrayList<Opiskelija>();

	lista.add(new Opiskelija("Olli Olio", "013632164"));
	lista.add(new Opiskelija("Melissa Metodi", "013401780"));
	lista.add(new Opiskelija("Saku Silmukka", "015696234"));
	lista.add(new Opiskelija("Essi Ehto", "013924429"));
	lista.add(new Opiskelija("Pasi Parametri", "016956834"));
	lista.add(new Opiskelija("Lasse Luokka", "012835085"));
	lista.add(new Opiskelija("Taina Taulukko", "014662803"));
	lista.add(new Opiskelija("Iiro Indeksi", "012807013"));

	// kirjoita koodia tähän
    }
}

Esimerkkitulostuksia:

Anna hakusana: ukk
Tulokset:
Saku Silmukka (015696234)
Taina Taulukko (014662803)
Anna hakusana: si
Tulokset:
Essi Ehto (013924429)
Pasi Parametri (016956834)
Iiro Indeksi (012807013)

Kello

Kellosta olio

Viikon 5 tehtäväsarjassa 2 tehtiin ensin ylhäältä rajoitettu laskuri ja rakennettiin laskurien avulla pääohjelmaan kello. Tehdään nyt myös itse kellosta olio. Luokan kello runko näyttää seuraavalta:


public class Kello {
    YlhaaltaRajoitettuLaskuri tunnit;
    YlhaaltaRajoitettuLaskuri minuutit;
    YlhaaltaRajoitettuLaskuri sekunnit;

    public Kello(int tunnitAlussa, int minuutitAlussa, int sekunnitAlussa) {
      // luodaan kello joka asetetaan parametrina annettuun aikaan
    }

    public void etene(){
      // kello etenee sekunnilla
    }

    public String toString() {
        // palauttaa kellon merkkijonoesityksen
    }
}

Lisää uuteen projektiin viimeviikkoinen laskuri-luokka ja yllä oleva kellon runko.

Toteuta konstruktori ja puuttuvat metodit. Voit testata kelloasi seuraavalla pääohjelmalla:

public class Main {
    public static void main(String[] args) throws Exception {
        Kello kello = new Kello(23, 59, 50);

        while( true ) {
            System.out.println( kello );
            kello.etene();
            // nukutaan sekunti
            Thread.sleep(1000);
        }
    }
}

Tulostuksen tulisi edetä seuraavasti:

23:59:50
23:59:51
23:59:52
23:59:53
23:59:54
23:59:55
23:59:56
23:59:57
23:59:58
23:59:59
00:00:00
00:00:01

Uudelleennimentää

Kellomme koostuu kolmesta ylhäältärajoitetusta laskurista. Oikeastaan laskurit ovat viisareita. Uudelleennimeämme luokan jolloin kello näyttää seuraavalta:

public class Kello {
    private Viisari tunnit;
    private Viisari minuutit;
    private Viisari sekunnit;

    // ...
}

Uudelleennimentä on NetBeansissa helppoa. Etsi ohje täältä. Vilkaise samalla onko ohjeessa jotain muita hyödyllisiä vihjeitä joitka eivät vielä ole repertuaarissasi. Kokeile samalla uudelleennimetä viisarin metodi seuraava kuvaavammin.

Luokan Henkilö muokkaaminen

Iän laskeminen syntymäpäivän perusteella

Materiaalissa lisättiin henkilölle oliomuuttujaksi syntymäpäivän kertova Paivays-olio. Samalla todettiin, että oliomuuttuja ika kannattaa poistaa sillä iän pystyy laskemaan päiväyksen ja syntymäpäivän avulla.

Toteuta metodi ika joka palauttaa henkilön iän.

import java.util.Calendar;

public class Henkilo {
    private String nimi;
    private Paivays syntymaPaiva;

    public Henkilo(String nimi, int pp, int kk, int vv) {
        this.nimi = nimi;
        this.syntymaPaiva = new Paivays(pp, kk, vv);
    }

    public int ika() {
        // laske henkilön ikä syntymäpäivän ja tämän päivän perusteella
        // tämä päivä saadaan selville seuraavasti
        // Calendar.getInstance().get(Calendar.DATE);
        // Calendar.getInstance().get(Calendar.MONTH) + 1; // tammikuun numero on 0 joten lisätään 1
        // Calendar.getInstance().get(Calendar.YEAR);
    }

    public String getNimi() {
        return nimi;
    }

    public String toString() {
        return nimi +", syntynyt "+ syntymaPaiva;
    }
}

Huom: voit ottaa pohjaksesi tehtävässä 1 laajennettu Päiväys-luokka ja miettiä miten saat hyödynnettyä päiväyksen metodia erotusVuosissa iän laskemiseen.

Voit testata Henkilöä seuraavalla pääohjelmalla. Lisää myös itsesi ohjelmaan ja varmista että ikäsi tulostuu oikein.

public class Main {
    public static void main(String[] args) {
        Henkilo pekka = new Henkilo("Pekka", 15, 2, 1993);
        Henkilo antti = new Henkilo("Antti", 1, 3, 1955);

        System.out.println( antti.getNimi() + " ikä " + antti.ika() + " vuotta");
        System.out.println( pekka.getNimi() + " ikä " + pekka.ika() + " vuotta");
    }
}

Tulostus:

Antti ikä 55 vuotta
Pekka ikä 18 vuotta

Iän vertailu syntymäpäivien perusteella

Tee henkilölle metodi jonka avulla se vertaa ikäänsä parametrina annettuun henkilöön. Jos henkilö on vanhempi eli syntynyt aiemmin, palauttaa metodi true ja muuten false.

public class Henkilo {
    // ...

    public boolean vanhempiKuin(Henkilo verrattava) {
       // vertaa henklöiden ikiä käyttäen henkilöiden syntymäpäivää
    }
}

Ja testaa laajennettua Henkilö-luokkaa esim. seuraavasti:

public class Main {
    public static void main(String[] args) {
        Henkilo pekka = new Henkilo("Pekka", 15, 2, 1983);
        Henkilo martin = new Henkilo("Martin", 1, 3, 1983);

        System.out.println( martin.getNimi() + " vanhempi kuin " +  pekka.getNimi() + ": "+ martin.vanhempiKuin(pekka) );
        System.out.println( pekka.getNimi() + " vanhempi kuin " +  martin.getNimi() + ": "+ pekka.vanhempiKuin(martin) );
    } 
}  

Tulostus:

Martin vanhempi kuin Pekka: false
Pekka vanhempi kuin Martin: true

Opiskelijoita kurssilla

Kurssi-luokka

Tee luokka Kurssi, johon tallennetaan kurssin nimi (String). Tee luokkaan seuraavat metodit:

Seuraava pääohjelma testaa luokan toimintaa:

public class Main {
    public static void main(String[] args) {
	Kurssi ohpe = new Kurssi("Ohjelmoinnin perusteet");
	System.out.println("Kurssi: " + ohpe.haeNimi());
    }
}

Ohjelman tulostus on seuraava:

Kurssi: Ohjelmoinnin perusteet

Opiskelijat

Lisää luokkaan seuraavat metodit:

Opiskelijat ovat tehtäväsarjan 3 mukaisia Opiskelija-olioita. Tallenna kurssilla olevat opiskelijat Kurssi-luokan sisäiseen ArrayList-listaan.

Seuraava pääohjelma testaa luokan toimintaa:

public class Main {
    public static void main(String[] args) {
	Kurssi ohpe = new Kurssi("Ohjelmoinnin perusteet");

	Opiskelija alfred = new Opiskelija("Alfred Apina", "017635727");
	Opiskelija bruno = new Opiskelija("Bruno Banaani", "011288989");

	ohpe.lisaaOpiskelija(alfred);
	ohpe.lisaaOpiskelija(bruno);
	ohpe.lisaaOpiskelija(new Opiskelija("Cecilia Cembalo", "013672548")); //vaikutus on sama kuin edellisillä

	ohpe.tulostaOpiskelijat();
    }
}

Ohjelman tulostuksen tulisi olla seuraava:

Alfred Apina (017635727)
Bruno Banaani (011288989)
Cecilia Cembalo (013672548)

Opiskelijoiden määrä

Lisää luokkaan seuraavat metodit:

Kurssin suurin sallittu opiskelijamäärä on oletusarvoisesti 25. Metodin asetaSuurinOpiskelijamaara avulla tätä rajaa voi muuttaa. Muuta metodia lisaaOpiskelija niin, että se ei lisää opiskelijaa kurssille, jos sallittu opiskelijamäärä ylittyisi.

Seuraava pääohjelma testaa luokan toimintaa:

public class Main {
    public static void main(String[] args) {
	Kurssi ohpe = new Kurssi("Ohjelmoinnin perusteet");

        ohpe.asetaSuurinOpiskelijamaara(3);

	Opiskelija alfred = new Opiskelija("Alfred Apina", "017635727");
	ohpe.lisaaOpiskelija(alfred);
	System.out.println("Opiskelijoita: " + ohpe.haeOpiskelijamaara());

	Opiskelija bruno = new Opiskelija("Bruno Banaani", "011288989");
	ohpe.lisaaOpiskelija(bruno);
	System.out.println("Opiskelijoita: " + ohpe.haeOpiskelijamaara());

	Opiskelija cecilia = new Opiskelija("Cecilia Cembalo", "013672548");
	ohpe.lisaaOpiskelija(cecilia);
	System.out.println("Opiskelijoita: " + ohpe.haeOpiskelijamaara());

        Opiskelija ylermi = new Opiskelija("Ylermi Ylimääräinen", "014711055");
        ohpe.lisaaOpiskelija(ylermi);
	System.out.println("Opiskelijoita: " + ohpe.haeOpiskelijamaara());
    }
}

Ohjelman tulostuksen tulisi olla seuraava:

Opiskelijoita: 1
Opiskelijoita: 2
Opiskelijoita: 3
Opiskelijoita: 3

Vihje: Tässä kannattanee kerrata materiaalin luku 20.

Opiskelijan etsiminen

Lisää luokkaan seuraava metodi:

Metodin tulee palauttaa Opiskelija-olio, joka vastaa annettua opiskelijanumeroa. Jos tällaista opiskelijaa ei ole, metodin tulee palauttaa null-viittaus.

Seuraava pääohjelma testaa luokan toimintaa:

import java.util.Scanner;

public class Main {
    private static Scanner lukija = new Scanner(System.in);

    public static void main(String[] args) {
	Kurssi ohpe = new Kurssi("Ohjelmoinnin perusteet");

	ohpe.lisaaOpiskelija(new Opiskelija("Alfred Apina", "017635727"));
	ohpe.lisaaOpiskelija(new Opiskelija("Bruno Banaani", "011288989"));
        ohpe.lisaaOpiskelija(new Opiskelija("Cecilia Cembalo", "013672548"));

        System.out.print("Anna opiskelijanumero: ");
        String opiskelijanumero = lukija.nextLine();
        Opiskelija etsittava = ohpe.etsiOpiskelija(opiskelijanumero);
        if (etsittava == null) {
            System.out.println("Opiskelijaa ei löytynyt!");
        } else {
            System.out.println(etsittava);
        }
	
    }
}

Esimerkkitulostuksia:

Anna opiskelijanumero: 011288989
Bruno Banaani (011288989)
Anna opiskelijanumero: 011288988
Opiskelijaa ei löytynyt!

Päiväys

Ajan kuluminen

Lisätään Paivays-olioon mahdollisuus edistää aikaa. Tee oliolle metodi Paivays paivienPaasta(int paivia), joka luo uuden Paivays-olion, jonka päiväys on annetun päivien lukumäärän verran suurempi kuin oliolla, jolle sitä kutsuttiin. Tässä vaiheessa voit olettaa, että jokaisessa kuukaudessa on 30 päivää. Huomaa, että vanhan päiväysolion on pysyttävä muuttumattomana! Ohessa on esimerkki metodin toiminnasta.

    public static void main(String[] args) {
        Paivays pvm = new Paivays(25, 2, 2011);
        Paivays uusi_pvm = pvm.paivienPaasta(7);
        for (int i = 1; i <= 7; ++i) {
            System.out.println("Perjantai " + i + " viikon kuluttua on " + uusi_pvm);
            uusi_pvm = uusi_pvm.paivienPaasta(7);
        }
        System.out.println("Tämän viikon perjantai on " + pvm);
        System.out.println("Päivämäärä 790:n päivän päästä tämän viikon perjantaista on  " + pvm.paivienPaasta(790));
  }

Ohjelma tulostaa:

Perjantai 1 viikon kuluttua on 2.3.2011
Perjantai 2 viikon kuluttua on 9.3.2011
Perjantai 3 viikon kuluttua on 16.3.2011
Perjantai 4 viikon kuluttua on 23.3.2011
Perjantai 5 viikon kuluttua on 30.3.2011
Perjantai 6 viikon kuluttua on 7.4.2011
Perjantai 7 viikon kuluttua on 14.4.2011
Tämän viikon perjantai on 25.2.2011
Päivämäärä 790:n päivän päästä tämän viikon perjantaista on  5.5.2013

Huom! Sen sijaan, että muuttaisimme vanhan olion tilaa palautamme uuden olion. Kuvitellaan, että Paivays-luokalle on olemassa metodi edista, joka toimii vastaavasti kuin ohjelmoimamme metodi, mutta se muuttaa vanhan olion tilaa. Tällöin seuraava koodin pätkä tuottaisi ongelmia.

Paivays nyt = new Paivays(20,2,2011);
Paivays viikonPaasta = nyt;
viikonPaasta.edista(7);
System.out.println("Nyt: " + nyt);
System.out.println("Viikon päästä: " + viikonPaasta);

Ohjelman tulostus olisi seuraavanlainen:

Nyt 27.2.2011
Viikon päästä 27.2.2011

Tämä johtuu siitä, että tavallinen sijoitus kopioi ainoastaan viitteen olioon. Siis itse asiassa ohjelman oliot nyt ja viikonPaasta viittavaat yhteen ja samaan Paivays-olioon.

Kuukausien päivien huomioiminen

Muunna edellisen tehtävän metodia siten, että se ottaa huomioon kunkin kuukauden päivät. Tässä vaiheessa voit jättää karkausvuoden huomiotta.

Jos talletat kuukausien päivät aputaulukkoon on kunkin kuukauden päivien lukumäärä helposti luettavissa. Muunna Paivays-luokan määrittelyä seuraavasti.

 
public class Paivays {
    // paikassa 0 = tammikuussa päiviä, 1 = helmikuussa päiviä jne...
    static int[] kuukaudessaPaivia = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    private int pv;
    private int kk;
    private int vv;
 
   /* Metodien määrittelyt */
}

Nyt edellisen tehtävän pääohjelman pitäisikin tulostaa seuraavasti:

Perjantai 1 viikon kuluttua on 4.3.2011
Perjantai 2 viikon kuluttua on 11.3.2011
Perjantai 3 viikon kuluttua on 18.3.2011
Perjantai 4 viikon kuluttua on 25.3.2011
Perjantai 5 viikon kuluttua on 1.4.2011
Perjantai 6 viikon kuluttua on 8.4.2011
Perjantai 7 viikon kuluttua on 15.4.2011
Tämän viikon perjantai on 25.2.2011
Päivämäärä 790:n päivän päästä tämän viikon perjantaista on  26.4.2013

Karkausvuoden huomioiminen

Huomio metodissa paivienPaasta karkausvuodet. Jos vuosi on karkausvuosi helmikuussa onkin 29 päivää. Jos vuosi jaollinen 400:lla tai se on jaollinen 4:llä, mutta ei 100:lla se on karkausvuosi. Esimerkiksi 2012 on karkausvuosi, mutta 2100 ei ole karkausvuosi. Kannattaa tehdä apumetodi boolean karkausvuosi(int vuosi). Kannattaako metodin olla staattinen vai ei-staattinen?

Edellisen tehtävän pääohjelman tulostus onkin nyt:

Perjantai 1 viikon kuluttua on 4.3.2011
...paljon tulostusta...
Päivämäärä 790:n päivän päästä tämän viikon perjantaista on  25.4.2013

Lotto

LottoRivi-olio

Tässä käytetään hyväksi edellä muokattua Paivays-luokkaa. Tee luokka LottoRivi, johon talletetaan yksittäiseen lottoriviin kuuluvat 7 numeroa. Lisäksi lottoriviin talletetaan arvonnan päivämäärä Paivays-olion muodossa. Lottonumeroden tallettamiseen kannattanee käyttää ArrayListia.

Tee luokalle seuraavat metodit, oliomuuttujat ja konstruktori.

Lottoriviä voi käyttää esimerkiksi seuraavalla tavalla.

   public static void main(String [] args) {
        Paivays pvm = new Paivays(19,2,2011);
        ArrayList<Integer> luvut = new ArrayList<Integer>();
        luvut.add(1); luvut.add(17); luvut.add(20);
        luvut.add(21); luvut.add(26); luvut.add(33);
        luvut.add(39);
        LottoRivi rivi = new LottoRivi(luvut, pvm);
        System.out.println(rivi);
    }

Tällöin ohjelma tulostaa:

Päivänä 19.2.2011 arvottiin luvut 1,17,20,21,26,33,39.

Lottorivien vertailu

Lottorivit ovat samat jos ne sisältävät samat numerot. Tee LottoRivi-oliolle metodi samatNumerotKuin joka palauttaa true, jos lottorivit ovat samat.

Metodia voidaan käyttää seuraavalla tavalla:

    public static void main(String [] args) {
        Paivays pvm = new Paivays(19,2,2011);
        ArrayList<Integer> luvut = new ArrayList<Integer>();
        luvut.add(1); luvut.add(17); luvut.add(20);
        luvut.add(21); luvut.add(26); luvut.add(33);
        luvut.add(39);
        LottoRivi rivi1 = new LottoRivi(luvut, pvm);
        luvut.remove(0); luvut.add(7);
        LottoRivi rivi2 = new LottoRivi(luvut, pvm.paivienPaasta(7));
        luvut.remove(6); luvut.add(1);
        LottoRivi rivi3 = new LottoRivi(luvut, pvm.paivienPaasta(7));
        if(rivi1.samatNumerotKuin(rivi2)) {
            System.out.println("Rivit 1 ja 2 olivat samat.");
        }
        if(rivi1.samatNumerotKuin(rivi3)) {
            System.out.println("Rivit 1 ja 3 olivat samat.");
        }
    }

Ohjelma tulostaa

Rivit 1 ja 3 olivat samat.

Huom! jos ohjelmasi kuvittelee, että myös rivit 1 ja 2 ovat samat, on vika todennäköisesti konstruktorissasi. Aidon ArrayListin kopioimisen sijaan kopioidaan LottoRivi-olioon kopioidaan luultavasti ainoastaan viite, jolloin kaikki luomasi rivit viittaavat samaan ArrayListiin. Tällöin muokatessasi ArrayListia muokkaat myös kaikkia lottorivejäsi. Uuden ArrayListin luonti vanhan pohjalta on helppoa ns. kopio-konstruktorin avulla:

  ArrayList<Integer> kopio = new ArrayList<Integer>(vanha);

Veikkaus

Luodaan Veikkaus-olio, joka järjestää viikottain lottoarvonnan. Tee Veikkaus-olio, jolla on seuraavanlainen konstruktori ja metodi.

Lottoarvontaa varten kannattanee tehdä Veikkaus-oliolle ArrayList-oliomuuttuja, jossa on kaikki mahdolliset lottoarvonnan numerot (1-39). Luotaessa uusi lottorivi, taulukko tämä lista sekoitetaan (Collections.shuffle) ja kopioidaan siitä sopiva osa liitettäväksi palautettavaan LottoRivi-olioon.

Luokkaa voit käyttää seuraavalla tavalla:

    public static void main(String[] args) {
        Veikkaus veikkaus = new Veikkaus(new Paivays(2,1,1971));
        for (int i = 0; i < 3; ++i) {
            System.out.println(veikkaus.lottoArvonta());
        }
    }

Ohjelman tulostus saattaisi olla seuraavanlainen:

Päivänä 2.1.1971 arvottiin luvut 3,11,13,15,16,19,23.
Päivänä 9.1.1971 arvottiin luvut 4,10,19,21,23,36,38.
Päivänä 16.1.1971 arvottiin luvut 3,8,10,20,24,26,32.

Arvottuja lukuja ei ole pakko pitää järjestyksessä.

Veikkaaja

Tehdään vuorovaikutteinen ohjelma, jossa kysytään aluksi käyttäjältä 7 numeroa lottoriviä varten. Tämän jälkeen luodaan Veikkaus-olio, joka arpoo lottorivejä niin kauan kunnes käyttäjä saa 7 oikein (eli kunnes arvottu rivi on sama kuin käyttäjän antama). Tämän jälkeen tulostetaan oikea rivi.

Ohjelman kulku saattaisi olla seuraavanlainen:

Anna 7 lukua
1
2
1
Luku 1 ei kelpaa, koska se on jo annettu tai se on liian pieni tai suuri.
15
40
Luku 40 ei kelpaa, koska se on jo annettu tai se on liian pieni tai suuri.
39
37
7
26
Aloitimme lottoamisen 19.2.2011
... ja vihdoinkin onnisti. Päivänä 8.1.76383 arvottiin luvut 1,2,7,15,26,37,39.

Loppuivatko tehtävät kesken? Ei hätää: saatavilla on lisätehtäväsarja Ohpesokkelo. Siinä tehdään graafinen labyrinttipeli!

Kurssipalaute

Vastaa kurssikyselyyn

Vastaa kurssikyselyyn osoitteessa http://ilmo.cs.helsinki.fi/kurssit/servlet/Valinta. Valitse Ohjelmoinnin perusteet.

Muista myös lähettää lomake! Lähetysnäppäin on lomakkeen lopussa. Vastaukset ovat anonyymejä, joten ei tarvitse ujostella...

Kyselylomakkeessa on joukko kohtia joihin ei voi syystä tai toisesta vastata. Jätä sellaiset kohdat väliin. Erityisesti koetta koskevat kysymykset ovat tällaisia. Samoin kysymys 15 "harjoitusryhmän pitäjästä" ei ole tässä kurssityylissä helppo.

Vastauksilla ihan oikeasti on merkitystä laitoksen opetusta kehitettäessä!