Kaikki ohjelmointitehtävät on tarkoitus tehdä tietokoneella!
Nämä ensimmäiset harjoitukset kertaavat Ohjelmoinnin perusteet -kurssin sisältöä. Tehtävät samalla myös esittelevät Java-ohjelmointi-kurssinn esitietovaatimuksia... Tämän verran siis Javaa oikeastaan on osattava jo ennen tätä kurssia! Eivät nämä tehtävät toki ihan helppoja ole Ohjelmoinnin perusteet -kurssin juuri suorittaneillekaan!
Aiheita: matriiseja, suunnittelua ja toteustusta, olioita taulukon alkioina, peruskäsitteitä, ...
public class Opiskelija { private String etunimi; private String sukunimi; private int koepisteet; // saa olla vain 0-50 private int harjoituspisteet; // saa olla vain 0-10 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ä luokkaa metodein
0-29 30-34 35-39 40-44 45-49 50-60 0 1 2 3 4 5
Voit ohjelmoida luokkaan muitakin hyödyllisiä metodeita.
Havainnollista Opiskelija-olioiden käyttötapaa luokkaan sijoitettavalla pääohjelmalla.
Huom: Asettavat ja ottavat aksessorit on nimetty Javan omassa kalustossa hyvin johdomukaisesti etuliitteillä "set" ja "get", esim. setValue, getValue. Asettavalle aksessorille luonteva suomalainen nimeämistapa olisi asetaArvo, mutta hyvää ottavan aksessorin nimeä on vaikeampi keksiä: otaArvo, annaArvo, haeArvo, ... Mikään noista ei ole kovin hyvä. Tässä tehtävässä käytetty englannin ja suomen sekoittaminenkin voi jotakuta häiritä. Pohdi asiaa. Mikä olisi mielestäsi paras ja selkein tapa?
Opiskelija[] opiskelijat = new Opiskelija[9];asettaa muuttujan arvoksi yhdeksänalkioisen taulukon, jonka alkiot voivat saada arvokseen Opiskelija-olioita. Esimerkiksi lause
opiskelijat[6] = new Opiskelija("Pekka", "Puupää");asettaa taulukon seitsemännen alkion arvoksi sellaisen Opiskelija-olion, jolle
opiskelijat[6].getEtunimi()on arvoltaan "Pekka" ja
opiskelijat[6].getPistesaalis()on alkuarvoltaan 0
Laadi sovellus, joka pyytää opiskelijoiden tiedot ja tulostaa ne pistejärjestyksessä arvosanoineen. Saman pistemäärän saaneet opiskelijat tulostetaan "aakkosjärjestyksessä". Käytä järjestämisen perusteena Opiskelija-luokan metodia compareTo. Toteuta tietojen kysely, järjestäminen ja tulostaminen yksityisinä luokkametodeina, "pääohjelman pikku apulaisina".
Tulostuksen ulkoasu on seuraavanlainen:
Tulos Nimi 5 50 Juonio, Jussi 5 50 Mainio, Matti 4 46 Vemmelsääri, Väiski 3 42 Ankka, Aku 3 42 Ankka, Taavi 3 40 Puupää, Pekka 2 39 Hirmuinen, Harald 1 31 Hopo, Hessu 0 15 Lipponen, Pekka
3.14
on arvo.3.14
on tyyppi.3.14
on muuttuja.int
-tyyppisen muuttujan arvo voi olla
negatiivinen.double
tarkoittaa kaksoistarkkuuden
liukulukutyyppiä.double
-tyyppisen muuttujan arvo voi olla
negatiivinen.
Aiheita: syötön ja tulostuksen ohjaamista, varottavia esimerkkejä, omia työkaluja, taulukko olion kenttänä, ... i/o:n kertausta, merkkien käsittelyä, käsitteiden tarkentamista
java Ohjelma < tiedostoSamoin ohjelman tulosteet voidaan ohjata tiedostoon kuvaruudun sijasta:
java Ohjelma > tiedostoMyös molemmat voidaan tehdä:
java Ohjelma < tiedosto1 > tiedosto2
Laadi ohjelma Humanisoi, joka tekee syöttötiedostosta sellaisen kopion, jossa jokainen numeromerkki on korvattu kauniilla tähtimerkillä '*'. Ohjelma suoritetaan komennolla:
java Humanisoi < alkuteksti.txt > humanoitu.txt
Vihjeitä
while (lukija.hasNextLine()) { String rivi = lukija.nextLine(); // ... käsittele yksi rivi }
Selvitä i:n ja a:n arvo seuraavien sijoituslauseiden jälkeen. Ajatellaan että lauseet suoritetaan erillisinä, ei peräkkäin. Mitään muuta ei tehdä määrittelyiden ja lauseen välissä. Varaudu selittämään miksi arvot muodostuvat sellaisiksi kuin muodostuvat, älä tyydy vain selvittämään arvoja tietokoneella.
a) a = ++i; b) a = i++; c) a = --i; d) a = i--; e) a += i++; f) a *= ++i + i++; g) a += i++ - --i; h) a -= (i++ - --a) + (++a + i--); i) a += (i = ++a) - (a -= i--);Mitä mieltä olet tällaisesta ohjelmoinnista? Tämä tehtävä on kuin rokotus; sen on tarkoitus tuottaa "vasta-aineita", jotta tuollainen ohjelmointityyli ei pääsisi jatkossa vaivaamaan...
Kun jokin ohjelmointikielen väline vaikuttaa helppokäyttöiseltä ja joustavalta, kyseessä on se, että joku jossakin on toteuttanut, 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, ne ovat synnyttyään muuttumattomia, "immutaabeleita". Ohjelmoi ja testaa oma merkkijonoluokka OmaString muutettavien merkkijononojen toteutukseksi. Tehtävän ratkaisemisessa on pyrittävä välttämään roskienkerääjän ylensyöttämistä. Itse asiassa Java-APIssa on luokat StringBuilder ja StringBuffer muutettavien merkkijonojen käsittelyyn, mutta asioiden tekeminen omin käsin on opettavaista!
Luokan OmaString määrittely alkaa:
public class OmaString { public static final int MAKSIMIPITUUS = 100; // pisin sallittu merkkijono // ("final" tarkoittaa vakiota); // tätä voi kysyä luokan ulkopuolelta // OmaString.MAKSIMIPITUUS private char[] mjono; // merkkijononon esitys char-taulukkona private int pituus; // montako alkioita mjono:n alusta on käytössä ...Luokassa on konstruktorit:
Luokka tarjoaa aksessorit:
Kirjoita pääohjelma, joka esittelee monipuolisesti OmaString-olioiden käyttöä.
Aiheita: luokkia ja kenttiä, luokkamuuttujia ja ilmentymämuuttujia, mallinnusta, suunnittelua
Alkueläimen perimä muodostuu kolmestakymmenestä nukleotidistä, joista kukin voi olla arvoltaan 'A', 'C', 'G' tai 'T'. Alkueläin lisääntyy joko jakautumalla tai pariutumalla toisen alkueläimen kanssa. Jakautumalla syntynyt alkueläin on yksilöllisyyttään lukuunottamatta täydellinen kopio vanhemmastaan. Pariutumalla syntynyt alkueläin perii vanhempiensa perimien yhdistelmän siten, että kunkin nukleotidin kohdalla molempien vanhempien nukleotidi on yhtä todennäköinen (=0.5). Toisinaan alkueläimen perimään voi tapahtua myös mutaatio, joka muuttaa alkueläimen yhden nukleotidin toiseksi.
Mallinna alkueläin luokkana Alkuelain.
Yksilöllisyyden voi toteuttaa siten, että luokkamuuttuja (private static int otuslaskuri) laskee luotujen ilmentymien määrää ja jokainen luotava olio saa ilmentymävakionsa (final int yksilöllisyys) arvoksi tuon laskurin arvon luontihetkellä. Yhden geenin arpajaiset voi toteuttaa ehdolla (Math.random()<0.5).
Luokassa on ainakin seuraavat välineet:
Laadi pääohjelma tai erillinen pääohjelmaluokka, joka esittelee Alkueläin-olioiden käyttöä.
Toteuta ohjelman toimintalogiikka ns. komentotulkkina (ks. kirjan kappale 3.4.4., s. 126, WWW-materiaalin 3.4:n kohta komentotulkki ). Huomaa ettei komentotulkkilogiikka välttämättä edellytä switch-lauseen käyttämistä.
Aiheita: kirjastoluokka, periytyminen, abstrakti luokka, rajapintaluokka, polymorfismi, ...
public static void tulVas(arvo, int leveys) public static void tulOik(arvo, int leveys)tulostavat arvon kenttään jonka leveys on leveys. Parametri arvo voi olla int, String, char- tai boolean. Metodeista ensimmäinen tulostaa arvon kentän vasempaan laitaan, jälkimmäinen oikeaan. Metodit eivät vaihda riviä! Jos arvo on kenttää leveämpi, kenttää laajennetaan, arvoa ei katkaista (Tämä on turvallisempaa! Miksi?)
Tuo lisäperimä määrää sukupuolen siten, että A- ja C- tossueläimet ovat naaraita, G- ja T- tossueläimet koiraita.
Tossueläin ei voi lisääntyä jakautumalla, eikä sitä vielä osata kloonata. Pariutumisessa tossueläimen alkueläimeltä kotoisin oleva perimä määräytyy samoin kuin alkueläimellä. Sukupuoligeeni määräytyy pariutumisyrityksen seurauksena seuraavasti (kirjain tarkoittaa lapsen perimän sukupuolen määräävän nukleotidin arvoa):
vanhemmat lapsi A ja G A A ja T T C ja G C C ja T GMuilla yhdistelmillä lasta ei tietenkään synny.
Mallinna tossueläin luokkana Tossuelain, joka on luokan Alkuelain aliluokka. Käytä mahdollisimman paljon hyväksi yliluokan määrittelyitä. Ohjelmoi luokkaan ainakin metodit:
Huom: Jos Alkuelain-luokkasi perimätaulukko oli private - niinkuin hyvä lähtökohta on - tämän tehtävän "geenien käsittelyssä" tulee ongelmia: Vaikka aliluokka periikin yliluokkansa privaattikaluston, se ei pääse siihen käsiksi!! Voit harkita Alkuelain-luokan private-määritellyn taulukon muuttamista protected-määritellyksi; tällöin kenttään pääsee käsiksi aliluokasta (ja omasta pakkauksesta). Tai voit pitää kentän ennallaan ja täydentää Alkuelain-luokkaa sopivilla "geeniteknologisilla" lisäaksessoreilla. Nämä on tietenkin luontevaa määritellä näkymään tasolla protected!
Laadi myös pieni ohjelma, joka havainnollistaa Tossuelain-olioiden käyttöä. Jos haluat, jaksat ja ehdit, voit toki laajentaa tehtävän 10 Geenilaboratorion tossueläintutkimukseen sopivaksi.)
Huom: Harjoitusten alkueläimillä ja tossueläimillä ei taida olla paljonkaan tekemistä todellisen biologian kanssa! ;-)
Vaatimuksia ja vihjeitä:
Aiheita: lisää periytymisestä ja rajapintaluokista...
Numerojonoja voidaan konstruoida ainoastaan parametrittomalla konstruktorilla:
public Numerojono()Konstruktori luo tyhjän numerojonon, siis tyhjän merkkijonon.
Jotkin aksessorit ovat perittyinä sellaisinaan käyttökelpoisia, toiset täytyy korvata uudella jonon numeerisuuden säilyttämiseksi. Lisäksi tarvitaan yksi uusi aksessori
public int value()joka palauttaa arvonaan this-numerojonon esittämän kokonaisluvun. Tyhjällä numerojonolla ei ole numeerista arvoa. Metodi palauttaa tuossa tilanteessa arvon -1. (Numerojonot voivat esittää ainoastaan ei-negatiivisia lukuja.)
Vihjeitä:
Rajapintaluokka Vertailtava olkoon
public interface Vertailtava { public boolean pienempi(Vertailtava toinen); // this-olio < toinen-olio public boolean isompi(Vertailtava toinen); // this-olio > toinen-olio public boolean yhtäSuuri(Vertailtava toinen); // this-olio = toinen-olio }Muokkaa oppimateriaalin luokka Pikkuvarasto (ks. luku2) ja tehtävän 7&8 luokka OmaString sellaisiksi, että ne toteuttavat rajapintaluokan Vertailtava. Päätä itse (ja määrittele täsmällisesti!), mitä näissä luokissa tarkoittavat järjestys ja yhtäsuuruus. (Varastojen järjestys voisi olla vaikkapa sellainen, että tuotenimien String-suuruusjärjestys on ensisijainen ja tuotteen määrä toissijainen järjestysperuste.)
Huom: Pikkuvarastojen vertailumetodien parametrin käsittelyssä on yksi ongelma, parametrin tyyppi. Sen pitää olla Vertailtava, mutta tämä tyyppi ei sisällä Pikkuvaraston niitä ominaisuuksia, joihin vertailu voi perustua. Ratkaisu on eksplisiittinen tyyppimuunnos, "cast":
public boolean pienempi(Vertailtava toinen) { Pikkuvarasto toka = (Pikkuvarasto)toinen; // nyt toka on kunnon Pikkuvarasto, jonka ominaisuuksia // pääsee käyttämään ...
Ohjelmoi luokkametodi järjestä, joka saa parametrinaan Vertailtava[]-tyyppisen taulukon ja järjestää sen nousevaan järjestykseen.
Metodin oikea käyttötapa on sellainen, jossa taulukkoon on viety vain keskenään saman tyyppisiä olioita. Jos taulukossa on vaikkapa sekaisin Pikkuvarasto- ja OmaString-olioita, käy huonosti! Miksi? Voisiko asialle tehdä jotakin? Onko mielestäsi Java-kielen tämä ominaisuus hyvä vai huono? Voisivatko asiat olla toisin? Miten? (Javan versio 1.5 toi kieleen ns. geneeriset tyypit, käännösaikaiset tyyppiparametrit, joiden avulla tämä ongelma voidaan voittaa tyylikkäästi.)
Laadi myös pieni ohjelma, joka havainnollistaa järjestä-metodin oikeaa käyttöä ja myös erityyppisten olioiden sekoittamisen kauheita seurauksia.
Aiheita: tiedostoja, poikkeuksia, geneeristen tietorakenteiden käyttöä, kurssikysely
Täydennä, toteuta ja suorita tietokoneella luentomateriaalin yksinkertaisia ohjelmaesimerkkejä siten, että ne havainnollistavat OmaOlio-luokan määrittelyä ja käyttöä siten, että konstruoinnin epäonnistuminen virheellisten parametrien takia hoidetaan a) erityisellä onkoKunnossa-tyyppisellä aksessorilla, b) staattisella luontimetodilla, c) tarkistettuja (checked) poikkeuksia heittäen, d) tarkistamattomia (unchecked) poikkeuksia heittäen. Pohdi millaisiin tilanteisiin eri tavat soveltuvat. (Tähän havainnollistus- ja pohdintatehtävään ei tule esimerkkiratkaisua.)
Jos esimerkiksi Juhanin Ahon romaanista Rautatie (tiedosto Rautatie.txt) etsitään sanoja, tulos voi näyttää seuraavalta:
Mitä merkkijonoa etsitään tiedostosta Rautatie.txt? veturi 551: veturin, joka vaunuja vetää, sen panee kanssa höyry liikkeelle. Siipien 3215: --Etumaiset on suuremmat ... veturin pyörät ... toiset pienemmät, Mitä merkkijonoa etsitään tiedostosta Rautatie.txt? kännykkä Mitä merkkijonoa etsitään tiedostosta Rautatie.txt? koira 838: --Kyllä oli, koira vieköön, liikaa kehumista! Jos oikein pistokkaalla 841: niin kyllä, koira vieköön, jälelle jäisi raskas rumilas, vaikka kuinka Mitä merkkijonoa etsitään tiedostosta Rautatie.txt?
Kirjoita luokkaan myös yksinkertainen pääohjelmametodi, joka havainnollistaa luokan ilmentymien luomista ja käyttämistä.
Vihjeitä: Seuraajaluettelotietorakenteen toteutuksessa kannattaa käyttää sellaista HashMap-oliota, joka esittää assosiaatioita String-merkkijonon ja siihen liittyvän ArrayList<String>-olion välillä. Luentomateriaalin pikku esimerkistä voi saada ideoita ja uskonvarmuutta rakenteiden sisältämien rakenteiden toteuttamiseen:
import java.util.*; public class HAEsimerkki { public static void main(String[] args) { HashMap<String, ArrayList<String>> seuraajat = new HashMap<String,ArrayList<String>>(); ArrayList<String> sanoja = new ArrayList<String>(); sanoja.add("kissa"); sanoja.add("hiiri"); sanoja.add("hevonen"); seuraajat.put("eläimiä", sanoja); ArrayList<String> lukuja = new ArrayList<String>(); lukuja.add("134"); lukuja.add("-23"); lukuja.add("9871"); seuraajat.put("lukuja", lukuja); System.out.println(seuraajat); } }Esimerkkiohjelma tulostaa toString()-metodin valinnan polymorfismin ansiosta:
{lukuja=[134, -23, 9871], eläimiä=[kissa, hiiri, hevonen]}
Ohjelma kysyy ensin tiedoston nimen ja tarjoaa sitten palvelun yksittäisen sanan seuraajien selvittämiseen. Testitarkoituksia varten myös koko seuraajaluettelo on syytä tulostaa.
Testiaineistoa ohjelman kokeiluun (kannattanee käyttää versioita, joista välimerkit on poistettu):