Nämä harjoitukset liittyvät oppimateriaalin luvun V Ohjelmointitekniikkaa: taulukoita, etsimistä, järjestämistä alilukuihin 7, 8, 9, 11 ja 12.
Harjoitustehtävien otsikoilla on värikoodaus: Vihreät tehtävät on syytä tehdä joka tapauksessa. Värittämättömiä ei ole ihan pakko tehdä, mutta nekin ovat hyvin hyödyllisiä ja myös vaikuttavat pisteisiin. Keltaiset tehtävät ovat vähän haastavampia. Nekin lasketaan mukaan harjoituspisteitä määrättäessä, mutta ilmankin niitä harjoituksista voi saada maksimipisteet.
Huom: Jokaisen ohjelmatiedoston alkuun on kirjoitettava kommenttina harjoituskerta, tehtävän numero ja tekijän nimi tyyliin:
// 1. harjoitukset, tehtävä 1.3, Oili Opiskelija
Huom: Ohjaajien eli pajamestarien sivulta löytyy hienoja testauksen apuvälineitä tehtävien tekemisen avuksi!
Luokan Opiskelija määrittely alkaa seuraavasti:
public class Opiskelija { private String etunimi; private String sukunimi; private int koepisteet; // saa olla vain 0-36 private int harjoituspisteet; // saa olla vain 0-24 public Opiskelija(String etunimi, String sukunimi) { this.etunimi = etunimi; this.sukunimi = sukunimi; this.koepisteet = 0; this.harjoituspisteet = 0; } //--------------------------- public String getEtunimi() { return this.etunimi; } public String getSukunimi() { return this.sukunimi; } public int getPistesaalis() { return this.koepisteet + this.harjoituspisteet; } //--------------------------- ... }
Täydennä luokkamäärittelyä metodein
0-29 30-34 35-39 40-44 45-49 50-60 0 1 2 3 4 5
Vemmelsääri, Väiski 46 4 Lipponen, Pekka 5 0 Pitkälampi-Lahnanen, Kall 37 2
Tässä esimerkissä Vemmelsäären pistekertymä on 46 ja hänen arvosanansa on 4, jne. Pitkälampi-Lahnasen Kallen etunimestä näkyy leikkautuneen viimeinen kirjain pois.
Sovitaan että koko nimelle varattu tulostuskenttä on aina 25 merkkiä. Jos "sukunimi, etunimi"-merkkijono on pidempi, se vain tylysti katkaistaan. Sovitaan myös, että pisteet ja arvosana ovat kolmen merkin levyisen kentän oikeassa laidassa.
Vihje: Yksi monista tavoista toteuttaa kenttään tulostaminen voidaan ohjelmoida omin käsin seuraavan esimerkin tapaan:
char[] kenttaTaulu; String nimi; String kentta; kenttaTaulu = " ".toCharArray(); nimi = "Vemmelsääri" + ", " + "Väiski"; for (int i = 0; i< nimi.length() && i< kenttaTaulu.length; ++i) kenttaTaulu[i] = nimi.charAt(i); // HUOMAA kaksiosainen toistoehto! kentta = new String(kenttaTaulu); System.out.println("*" + kentta + "*"); kenttaTaulu = " ".toCharArray(); nimi = "Pitkälampi-Lahnanen" + ", " + "Kalle"; for (int i = 0; i< nimi.length() && i< kenttaTaulu.length; ++i) kenttaTaulu[i] = nimi.charAt(i); // HUOMAA kaksiosainen toistoehto! kentta = new String(kenttaTaulu); System.out.println("*" + kentta + "*");
Tulostus:
*Vemmelsääri, Väiski * *Pitkälampi-Lahnanen, Kall*
Ei ehkä olisi huonompi idea ohjelmoida toStrigille apumetodia
private String vieKenttaan(String tama, int kentanLeveys, boolean vasempaanLaitaan)
Tässa tapauksessa varmaankin kannattaisi ylläolevan esimerkin kenttaTaulu luoda ensin kentän leveyden kokoiseksi ja alustaa se välilyöntimerkein.
Havainnollista luokan ilmentymien käyttöä.
Opiskelija-olioille määritellään järjestys seuraavasti:
Täydennä Opiskelija-luokkaa metodilla public int compareTo(Opiskelija verrattava), joka vertaa this-opiskelijaa parametriopiskelijaan. Metodi palauttaa arvon -1, jos this-opiskelija edeltää verrattava-opiskelijaa, arvon +1 päinvastaisessa tapauksessa. Jos opiskelijat ovat kaikilta tiedoiltaan samat, metodi palauttaa arvon 0. String-olioiden vertailussa tyydytään siis siihen "aakkosjärjestykseen", jonka String-luokan compareTo-metodi määrittelee.
Havainnollista vertailun toimintaa.
Luokkaa Opiskelija voidaan käyttää seuraavaan tapaan taulukon alkiotyyppinä:
Ohjelmarivi
Opiskelija[] opiskelijat = new Opiskelija[9];
asettaa muuttujan arvoksi yhdeksänalkioisen taulukon, jonka alkiot voivat saada arvokseen Opiskelija-olioita. Tämän jälkeen esimerkiksi lause
opiskelijat[6] = new Opiskelija("Pekka", "Puupää");
asettaa taulukon seitsemännen alkion arvoksi sellaisen Opiskelija-olion, jolle
opiskelijat[6].getEtuimi()
on arvoltaan "Pekka" ja
opiskelijat[6].getPistesaalis()
on alkuarvoltaan 0
Laadi sovellus, joka pyytää opiskelijoiden tiedot ja tulostaa kurssin tulokset syöttöjärjestyksessä
Toteuta tietojen kysely ja tulostaminen yksityisinä luokkametodeina, "pääohjelman pikku apulaisina".
Tulostuksen ulkoasu on seuraavanlainen:
Hopo, Hessu 31 1 Ankka, Aku 42 3 Juonio, Jussi 50 5 Mainio, Matti 50 5 Puupää, Pekka 40 3 Lipponen, Pekka 5 0 Ankka, Taavi 42 3 Vemmelsääri, Väiski 46 4 Hirmuinen, Harald 39 2
Kehittele edellisen tehtävän ohjelmaa siten, että kurssin tulokset kirjoitetaan opiskelijoiden nimen mukaisessa järjestyksessä.
Käytä järjestämisessä vertailun välineenä Opiskelija-luokan metodia compareTo.
Toteuta myös järjestäminen yksityisenä luokkametodina, "pääohjelman pikku apulaisena".
Tulostuksen ulkoasu on nyt seuraavanlainen:
Ankka, Aku 42 3 Ankka, Taavi 42 3 Hirmuinen, Harald 39 2 Hopo, Hessu 31 1 Juonio, Jussi 50 5 Lipponen, Pekka 5 0 Mainio, Matti 50 5 Puupää, Pekka 40 3 Vemmelsääri, Väiski 46 5
Toteuta luokka Kurssi käyttäen piilossa pidettynä tietorakenteena Opiskelija-taulukkoa:
Toteuta sitten edellinen tehtävä käyttäen luokkaa Kurssi.
Huomaa miten tässä asiat hoidellaan edellistä tapaa "oliohenkisemmin". Edellinen tehtävä on myös vaivaton toteuttaa, jos ensin ohjelmoi tämän tehtävän!
Oikeasti Kurssi-luokkaan varmaan ohjelmoitaisiin monia muitakin kurssikirjanpidon kannalta hyödyllisiä aksessoreita.
Laajenna Kurssi-luokkaa metodilla
null
. Vihje:
Luo metodissa väliaikainen Opiskelija-olio ja
käytä compareTo-metodin palauttamaa tietoa samuuden havaitsemiseen.
Esittele laajennuksen toimintaa. Kokeile myös voitko muuttaa haetun opiskelijan koe- ja harjoituspistemäärää? Miksi tämä toimii/ei toimi?
Mieti onko etsiOpiskelija järkevä metodi vai olisiko Kurssilla parempi olla erikoistuneempia metodeja tyyliin:
public int opiskelijanArvosana(String etunimi, String sukunimi) public void muutaOpiskelijanKoepisteitä(String etunimi, String sukunimi, int uusiPistemaara)
Kun jokin ohjelmointikielen väline vaikuttaa helppokäyttöiseltä ja joustavalta, kyseessä on se, että joku on toteuttanut eli ohjelmoinut, tuon helppouden! Eikä se välttämättä ole ollut helppoa...
Luokassa String on monenlaisia välineitä merkkijonojen käsittelyyn. Kerran luotu String-olio tunnetusti ei kuitenkaan voi koskaan muuttua – String-oliot ovat kerran synnyttyään muuttumattomia, "immutaabeleita".
Tässä tehtäväsarjassa toteutetaan vaiheittain merkkijonoluokka MyString muutettavien merkkijononojen toteutukseksi. Tehtävienn ratkaisemisessa on pyrittävä välttämään roskienkerääjän ylensyöttämistä. Itse asiassa Java-APIssa on olemassa luokat StringBuilder ja StringBuffer muutettavien merkkijonojen käsittelyyn, mutta asioiden tekeminen omin käsin on opettavaista!
MyString-luokka kapseloi sisäänsä merkkitaulukon, jonka alkuosassa säilytetään esitettävää merkkijonoa. Luokan määrittely alkaa seuraavasti:
public class MyString { public static final int MAKSIMIPITUUS = 100; // pisin mahdollinen merkkijono // näkyy kaikkialle: MyString.MAKSIMIPITUUS private char[] mjono; // merkkijononon esitys char-taulukkona private int pituus; // montako alkioita mjono:n alusta on käytössä ... // eli ensimmäisen käyttämättömän indeksi }
Määrejono public static final
määrää, että
kenttä MAKSIMIPITUUS
näkyy kaikkialle, että
se liittyy luokkaan, ei olioon, ja että sen arvoa ei voi muuttaa.
Tällaiset nimetyt vakiot on tapana kirjoittaa isoin kirjaimin.
Toteutetaan ensin seuraava hyvin nysä API:
pituus
, ei taulukon mjono
pituus.
Kokeile ja esittele MyStringin käyttöä pienellä ohjelmalla.
Täydennä MyStringin toteutusta seuraavilla välineillä:
pituus-1
.
Virheellinen indeksointi aiheuttaa
poikkeuksen, jonka voi heittää vaikkapa lauseella
throw new IndexOutOfBoundsException("charAt-operaatiossa");
index
merkin uudella.
Sallitut indeksin arvot ovat
0—pituus-1
.
Virheellinen indeksointi aiheuttaa
poikkeuksen, jonka voi heittää vaikkapa lauseella
throw new IndexOutOfBoundsException("replace-operaatiossa");
this-olion kaikki old
-merkit
new
-merkeillä.
old
-merkit
new
-merkeillä.
Kokeile ja esittele kehitellyn MyStringin käyttöä pienellä ohjelmalla.
Täydennä MyStringin toteutusta seuraavilla välineillä:
index
.
Virheellinen indeksointi aiheuttaa
poikkeuksen, jonka voi heittää vaikkapa lauseella
throw new IndexOutOfBoundsException("deleteCharAt-operaatiossa");
Huomaa, että poistettua merkkiä seuranneet merkit myös pitää siirtää
taulukon alkua kohden.
offset
on se indeksi, jonka
kohtaan merkki lisätään. Tuolloin siis merkkijonon loppuosaa joudutaan
siirtämään yhtä pykälää edemmäksi.
Sallitut lisäyskohdat ovat siis 0–pituus
ellei mennä mjono
-taulukon omasta ylärjasta yli.
Virheellinen indeksointi aiheuttaa
poikkeuksen, jonka voi heittää vaikkapa lauseella
throw new IndexOutOfBoundsException("insert-operaatiossa");
Kokeile ja esittele kehitellyn MyStringin käyttöä pienellä ohjelmalla.
Täydennä MyStringin toteutusta String-luokasta tutuin aksessorein compareTo ja indexOf(char).
Poiketen String luokan compareTo-metodista riittää, että MyStringin compareTo-metodi palauttaa vain jonkin arvoista -1, 0 tai 1.
Kokeile ja esittele kehitellyn MyStringin käyttöä pienellä ohjelmalla.
Täydennä MyStringin toteutusta String-luokasta tutulla aksessorilla indexOf(MyString).
Kokeile ja esittele kehitellyn MyStringin käyttöä pienellä ohjelmalla.