Pakolliset tehtävät on merkitty harmaalla taustavärillä. Pakollisuus tarkoittaa, että kyseiset tehtävät ovat erityisen oleellisia ja niiden tekeminen on hyvin suositeltavaa. Jos joskus jokin "pakollinen" tehtävä jää tekemättä, kurssi ei kuitenkaan kaadu siihen.
Tässä tehtäväsarjassa tehdään luokat Tavara
ja Matkalaukku
,
joiden avulla käsitellään matkalaukussa olevia tavaroita.
Tee luokka Tavara
, josta muodostetut oliot vastaavat erilaisia tavaroita.
Tallennettavat tiedot ovat tavaran nimi ja paino (kg).
Lisää luokkaan seuraavat metodit:
haeNimi
, joka palauttaa tavaran nimen
haePaino
, joka palauttaa tavaran painon
toString
, joka palauttaa merkkijonon muotoa "nimi (paino kg)"
Seuraavassa on luokan käyttöesimerkki:
public class Main { public static void main(String[] args) { Tavara kirja = new Tavara("Aapiskukko", 2); Tavara puhelin = new Tavara("Nokia 3210", 1); System.out.println("Kirjan nimi: " + kirja.haeNimi()); System.out.println("Kirjan paino: " + kirja.haePaino()); System.out.println("Kirja: " + kirja); System.out.println("Puhelin: " + puhelin); } }
Ohjelman tulostuksen tulisi olla seuraava:
Kirjan nimi: Aapiskukko Kirjan paino: 2 Kirja: Aapiskukko (2 kg) Puhelin: Nokia 3210 (1 kg)
Tee luokka Matkalaukku
, johon liittyy maksimipaino.
Lisää luokkaan seuraavat metodit:
lisaaTavara
, joka lisää tavaran matkalaukkuun
toString
, joka palauttaa merkkijonon muotoa "x tavaraa (y kg)"
Tallenna tavarat seuraavaan ArrayList
-rakenteeseen:
ArrayList<Tavara> tavarat = new ArrayList<Tavara>();
Luokan Matkalaukku
tulee valvoa,
että sen tavaroiden yhteispaino ei ylitä maksimipainoa.
Jos maksimipaino ylittyisi uuden tavaran vuoksi,
metodi lisaaTavara
ei saa lisätä tavaraa.
Seuraavassa on luokan käyttöesimerkki:
public class Main { public static void main(String[] args) { Tavara kirja = new Tavara("Aapiskukko", 2); Tavara puhelin = new Tavara("Nokia 3210", 1); Tavara tiiliskivi = new Tavara("tiiliskivi", 4); Matkalaukku matkalaukku = new Matkalaukku(5); System.out.println(matkalaukku); matkalaukku.lisaaTavara(kirja); System.out.println(matkalaukku); matkalaukku.lisaaTavara(puhelin); System.out.println(matkalaukku); matkalaukku.lisaaTavara(tiiliskivi); System.out.println(matkalaukku); } }
Ohjelman tulostuksen tulisi olla seuraava:
0 tavaraa (0 kg) 1 tavaraa (2 kg) 2 tavaraa (3 kg) 2 tavaraa (3 kg)
Ilmoitukset "0 tavaraa" ja "1 tavaraa" eivät ole kovin hyvää suomea
– paremmat muodot olisivat "ei tavaroita" ja "1 tavara".
Tee tämä muutos luokkaan Matkalaukku
.
Nyt edellisen ohjelman tulostuksen tulisi olla seuraava:
ei tavaroita (0 kg) 1 tavara (2 kg) 2 tavaraa (3 kg) 2 tavaraa (3 kg)
Lisää luokkaan Matkalaukku
seuraavat metodit:
tulostaTavarat
, joka tulostaa kaikki matkalaukussa olevat tavarat
yhteispaino
, joka palauttaa tavaroiden yhteispainon
Seuraavassa on luokan käyttöesimerkki:
public class Main { public static void main(String[] args) { Tavara kirja = new Tavara("Aapiskukko", 2); Tavara puhelin = new Tavara("Nokia 3210", 1); Tavara tiiliskivi = new Tavara("tiiliskivi", 4); Matkalaukku matkalaukku = new Matkalaukku(10); matkalaukku.lisaaTavara(kirja); matkalaukku.lisaaTavara(puhelin); matkalaukku.lisaaTavara(tiiliskivi); System.out.println("Matkalaukussa on seuraavat tavarat:"); matkalaukku.tulostaTavarat(); System.out.println("Yhteispaino: " + matkalaukku.yhteispaino() + " kg"); } }
Ohjelman tulostuksen tulisi olla seuraava:
Matkalaukussa on seuraavat tavarat: Aapiskukko (2 kg) Nokia 3210 (1 kg) Tiiliskivi (4 kg) Yhteispaino: 7 kg
Lisää vielä luokkaan Matkalaukku
metodi raskainTavara
,
joka palauttaa painoltaan suurimman tavaran. Jos yhtä raskaita tavaroita on useita,
metodi voi palauttaa minkä tahansa niistä. Metodin on tarkoitus palauttaa viittaus olioon.
Seuraavassa on luokan käyttöesimerkki:
public class Main { public static void main(String[] args) { Tavara kirja = new Tavara("Aapiskukko", 2); Tavara puhelin = new Tavara("Nokia 3210", 1); Tavara tiiliskivi = new Tavara("tiiliskivi", 4); Matkalaukku matkalaukku = new Matkalaukku(10); matkalaukku.lisaaTavara(kirja); matkalaukku.lisaaTavara(puhelin); matkalaukku.lisaaTavara(tiiliskivi); Tavara raskain = matkalaukku.raskainTavara(); System.out.println("Raskain tavara: " + raskain); } }
Ohjelman tulostuksen tulisi olla seuraava:
Raskain tavara: tiiliskivi (4 kg)
Lisää luokkaan Matkalaukku
metodi poimiTavara
,
joka palauttaa matkalaukusta satunnaisen tavaran.
Metodin ei ole tarkoitus poistaa tavaraa matkalaukusta,
vaan ainoastaan palauttaa viittaus olioon.
Seuraavassa on luokan käyttöesimerkki:
public class Main { public static void main(String[] args) { Tavara kirja = new Tavara("Aapiskukko", 2); Tavara puhelin = new Tavara("Nokia 3210", 1); Tavara tiiliskivi = new Tavara("tiiliskivi", 4); Matkalaukku matkalaukku = new Matkalaukku(10); matkalaukku.lisaaTavara(kirja); matkalaukku.lisaaTavara(puhelin); matkalaukku.lisaaTavara(tiiliskivi); Tavara satunnainen = matkalaukku.poimiTavara(); System.out.println("Satunnainen tavara: " + satunnainen); } }
Ohjelman mahdollisia tulostuksia ovat seuraavat:
Satunnainen tavara: Aapiskukko (2 kg)
Satunnainen tavara: Nokia 3210 (1 kg)
Satunnainen tavara: tiiliskivi (4 kg)
Tässä tehtäväsarjassa tehdään luokka Lastiruuma
,
joka vastaa matkalaukkuja sisältävää lastiruumaa.
Tee luokka Lastiruuma
, johon liittyvät seuraavat metodit:
lisaaMatkalaukku
, joka lisää matkalaukun lastiruumaan
toString
, joka palauttaa merkkijonon muotoa "x matkalaukkua (y kg)"
Tallenna matkalaukut sopivaan ArrayList
-rakenteeseen.
Luokan Lastiruuma
tulee valvoa,
että sen matkalaukkujen yhteispaino ei ylitä maksimipainoa.
Jos maksimipaino ylittyisi uuden matkalaukun vuoksi,
metodi lisaaMatkalaukku
ei saa lisätä matkalaukkua.
Seuraavassa on luokan käyttöesimerkki:
public class Main { public static void main(String[] args) { Tavara kirja = new Tavara("Aapiskukko", 2); Tavara puhelin = new Tavara("Nokia 3210", 1); Tavara tiiliskivi = new Tavara("tiiliskivi", 4); Matkalaukku matinLaukku = new Matkalaukku(10); matinLaukku.lisaaTavara(kirja); matinLaukku.lisaaTavara(puhelin); Matkalaukku pekanLaukku = new Matkalaukku(10); pekanLaukku.lisaaTavara(tiiliskivi); Lastiruuma lastiruuma = new Lastiruuma(1000); lastiruuma.lisaaMatkalaukku(matinLaukku); lastiruuma.lisaaMatkalaukku(pekanLaukku); System.out.println(lastiruuma); } }
Ohjelman tulostuksen tulisi olla seuraava:
2 matkalaukkua (7 kg)
Lisää luokkaan Lastiruuma
metodi
tulostaTavarat
, joka tulostaa kaikki
lastiruuman matkalaukuissa olevat tavarat.
Seuraavassa on luokan käyttöesimerkki:
public class Main { public static void main(String[] args) { Tavara kirja = new Tavara("Aapiskukko", 2); Tavara puhelin = new Tavara("Nokia 3210", 1); Tavara tiiliskivi = new Tavara("tiiliskivi", 4); Matkalaukku matinLaukku = new Matkalaukku(10); matinLaukku.lisaaTavara(kirja); matinLaukku.lisaaTavara(puhelin); Matkalaukku pekanLaukku = new Matkalaukku(10); pekanLaukku.lisaaTavara(tiiliskivi); Lastiruuma lastiruuma = new Lastiruuma(1000); lastiruuma.lisaaMatkalaukku(matinLaukku); lastiruuma.lisaaMatkalaukku(pekanLaukku); System.out.println("Ruuman matkalaukuissa on seuraavat tavarat:"); lastiruuma.tulostaTavarat(); } }
Ohjelman tulostuksen tulisi olla seuraava:
Ruuman matkalaukuissa on seuraavat tavarat: Aapiskukko (2 kg) Nokia 3210 (1 kg) tiiliskivi (4 kg)
Testataan vielä, että lastiruuman toiminta on oikea eikä maksimipaino pääse ylittymään. Tee ohjelma, joka lisää lastiruumaan 100 matkalaukkua, joissa jokaisessa on yksi tiiliskivi. Tiiliskivien painot ovat 1, 2, 3, ..., 100 kg.
Ohjelman runko on seuraava:
public class Main { public static void main(String[] args) { Lastiruuma lastiruuma = new Lastiruuma(1000); // 100 matkalaukun lisääminen System.out.println(lastiruuma); } }
Ohjelman tulostuksen tulisi olla seuraava:
44 matkalaukkua (990 kg)
Tässä tehtäväsarjassa tehdään sanakirja,
josta voi hakea suomen kielen sanoille käännöksiä englannin kielelle.
Sanakirjan tekemisessä käytetään HashMap
-tietorakennetta.
Sanakirjan sanat tallennetaan seuraavaan HashMap
-rakenteeseen:
HashMap<String, String> sanakirja = new HashMap<String, String>();
Sanakirjassa on aluksi kolme sanaa:
sanakirja.put("apina", "monkey"); sanakirja.put("banaani", "banana"); sanakirja.put("cembalo", "harpsichord");
Tee ohjelma, joka kysyy käyttäjältä suomenkielisen sanan ja ilmoittaa sen englanninkielisen vastineen:
Anna sana: banaani Käännös: banana
Anna sana: selleri Tuntematon sana!
Lisää ohjelmaan toimintovalikko, jossa on kaksi komento:
komento käännä
kääntää sanan suomesta englanniksi ja
komento lopeta
poistuu ohjelmasta.
Tervetuloa käännösohjelmaan! Komennot: käännä käännös suomesta englanniksi lopeta ohjelman lopetus Komento: käännä Anna sana: cembalo Käännös: harpsichord Komento: käännä Anna sana: oboe Tuntematon sana! Komento: käännä Anna sana: apina Käännös: monkey Komento: lopeta Hei hei!
Lisää ohjelmaan komento lisää
,
jonka avulla käyttäjä voi lisätä uuden sanan sanakirjaan.
Tervetuloa käännösohjelmaan! Komennot: käännä käännös suomesta englanniksi lisää uuden sanan lisääminen lopeta ohjelman lopetus Komento: käännä Anna sana: selleri Tuntematon sana! Komento: lisää Suomeksi: selleri Käännös: celery Komento: käännä Anna sana: selleri Käännös: celery Komento: lopeta Hei hei!
Ohjelman testaaminen käsin on toivottoman työlästä. Syötteen antaminen on kuitenkin mahdollista automatisoida, esim. seuraavassa esitettävällä tekniikalla.
Lisää pääohjelmasi alkuun metodikutsu
automatisoiSyote()
:
public static Scanner lukija = new Scanner(System.in); public static void main(String[] args) { automatisoiSyote(); ... }
Kopioi metodi ohjelmatiedostoosi mainin jälkeen (mutta mainin ulkopuolelle):
public static void main(){ // ... } private static void automatisoiSyote() { String syote = "käännä\n" + "apina\n" + "käännä\n" + "juusto\n" + "lisää\n" + "juusto\n" + "cheese\n" + "käännä\n" + "juusto\n" + "lopeta\n"; lukija = new Scanner( new ByteArrayInputStream(syote.getBytes()) ); }
Joudut myös lisäämään ohjelmatiedostosi ylälaitaan importin:
import java.io.ByteArrayInputStream;
Metodi korvaa Javan Scannerin, eli syötteen lukemisesta huolehtivan olion versiolla, jolle syöte annetaan merkkijonona. Merkkijonomuuttujan syote
sisältö siis "simuloi" käyttäjän antamaa syötettä. Rivinvaihto syötteeseen merkitään \n
:llä. Jokainen yksittäinen rivinvaihtomerkkiin loppuva osa syote
-merkkijonossa siis vastaa käyttäjän yhteen nextLine()-komentoon antamaa syötettä.
Testityötettä on helppo muuttaa, esim. seuraavassa syötetään lisää uusia sanoja sanakirjaan:
String syote = "lisää\n" + "juusto\n" + "cheese\n" + "lisää\n" + "olut\n" + "beer\n" + "lisää\n" + "kirja\n" + "book\n" + "lisää\n" + "tietokone\n" + "computer\n" + "lisää\n" + "auto\n" + "car\n" + "lopeta\n";
Kun haluat testata ohjelmasi toimintaa jälleen käsin, kommentoi pois mainin alussa oleva metodikutsu automatisoiSyote()
Ohjelman toiminnan oikeellisuus pitää edelleen tarkastaa itse ruudulta. Tulostus voi olla aluksi hieman hämmentävää, sillä automatisoitu syöte ei näy ruudulla ollenkaan.
Lopullinen tavoite on automatisoida myös ohjelman tulostuksen oikeellisuden tarkastaminen niin hyvin, että ohjelman testaus ja testituloksen analysointi onnistuu "nappia painamalla". Palaamme aiheeseen myöhemmin kurssin aikana.
Lisää ohjelmaan vielä komento kaikki
,
joka tulostaa koko sanakirjan sisällön.
Tervetuloa käännösohjelmaan! Komennot: kaikki kaikkien sanojen listaus käännä käännös suomesta englanniksi lisää uuden sanan lisääminen lopeta ohjelman lopetus Komento: kaikki apina = monkey banaani = banana cembalo = harpsichord Komento: lopeta Hei hei!
Sanakirjaohjelmasi toimii nyt. Koodisi ei ehkä kuitenkaan ole maksimaalisen siistiä ja jos ohjelmaa laajennettaisiin uusilla toiminnoilla, uhkaisi koodi muuttua yhä sekavammaksi.
Pidetään sanakirjan toiminnallisuus aluksi ennallaan, mutta parannellaan ohjelman rakennetta.
Kirjoitit todennäköisesti koko ohjelmakoodin mainin sisään. Aloitetaan siivoaminen siirtämällä sanakirjatoiminnallisuus omaan luokkaan nimeltä Sanakirja
. Luokalla on aluksi seuraavat metodit:
String kaanna(String sana)
metodi palauttaa parametrinsa käännöksen. Jos sanaa ei tunneta, palautetaan null.Tässä vaiheessa siis ei vielä tueta komentoa jonka avulla on mahdollista tulostaa koko sanakirjan sisältö.
Tee muutos ja varmista että ohjelmasi toimii edelleen. Tehtävän 3.4 kuvaamasta testauksen automatisoinnista voi nyt olla apua.
Lisää sanakirjalle vielä seuraava metodi
ArrayList < String > kaannoksetListana()
Metodi palauttaa sanakirjan sisällön listana apina = monkey muotoisia merkkijonoja.
Testaa, että myös komento kaikki toimii uudella sanakirjalla.
Nyt sanakirja on saatu eristettyä pääohjelman seasta omaksi selkeän vastuun omaavaksi luokakseen. Olemme toimineet tässä Ohpen materiaalin luvussa 17 esitellyssä hengessä, eli eristäneet koodista käsitteen omaksi olioksi.
Siistitään vielä pääohjelmaa.
Tehdään siistiminen jakamalla pääohjelman toiminnallisuus selkeisiin (staattisiin) apumetodeihin, siten että main kutsuu yksittäisiä metodeja jotka huolehtivat erillisten komentojen toimenpiteiden suorittamisesta.
Pyri siihen, että ohjelmasi main näyttää suunilleen seuraavanlaiselta:
public static void main(String[] args) { automatisoiSyote(); Sanakirja sanakirja = alusta(); tulostaTervehdysJaKomennot(); while ( true ) { System.out.print("Komento: "); String komento = lukija.nextLine(); if ( komento.equals("käännä")) { kaannos( sanakirja ); } else if ( komento.equals("lisää") ) { lisays(sanakirja); } else if ( komento.equals("kaikki") ) { kaikkienTulostus(sanakirja); } else if ( komento.equals("lopeta") ) { break; } System.out.println(""); } }
Esim. metodi alusta
luo sanakirjaolion ja laittaa sinne alustavan sisällön sekä palauttaa sanakirjaolion pääohjelmalle. Metodin sisältö voi olla seuraava:
private static Sanakirja alusta() { Sanakirja sanakirja = new Sanakirja(); sanakirja.lisaa("apina", "monkey"); sanakirja.lisaa("banaani", "banana"); sanakirja.lisaa("cembalo", "harpsichord"); return sanakirja; }
Tekemiemme muutosten jälkeen uuden komennon lisääminen on helppoa. Toteuta ohjelmaan komento lukumäärä, jonka avulla käyttäjä voi tarkastaa sanakirjassa olevien käännösten määrän.
Tee laajennus siten, että ohjelman rakenne pysyy siistinä: lisää luokalle Sanakirja
sopiva metodi, esim. int lukumaara()
, ja lisää pääohjelmalle uusi apumetodi jota kutsutaan whilen sisältä suorittamaan varsinainen tulostustoimenpide.
Tehdään ohjelmaan radikaalihko muutos: mahdollistetaan tilanne jossa sanalla on useita käännöksiä.
Edellä käytetty HashMap jossa sanaa vastaa yksi käännös ei ole tässä tilanteessa paras mahdollinen. Parempi ratkaisu on käyttää Sanakirja-luokassa HasMap:ia, jossa yksittäistä sanaa vastaa ArrayList:illinen merkkijonoja:
public class Sanakirja { private HashMap <String, ArrayList < String > > sanakirja; // ...
Muuta nyt Sanakirja-luokan sisäistä rakennetta siten, että yhtä sanaa kohti voi olla useita käännöksiä. Tässä tehtävässä riittää että saat toimimaan metodit
String kaanna(String sana)
Huomaa, että laajennus ei vaikuta ollenkaan muuihin ohjelmanosiin kuin Sanakirja-luokkaan. Jos emme olisi putsanneet koodia, olisi muutoksia tehtävä pääohjelman sekaan ja riski virheen tekemiselle olisi huomattavasti suurempi.
Huom: kun sanalle lisätään ensimmäinen käännös, täytyy ensin luoda sanaa vastaavat käännökset tallettava ArrayList-olio seuraavaan tyyliin:
public void lisaa(String sana, String kaannos){ if ( sanakirja.get(sana)==null ) sanakirja.put(sana, new ArrayList()); // ... }
Viimeistele edellisessä tehtävässä aloittamasi muutos siten, että muutkin Sanakirja-luokan metodit toimivat uudessa tilanteessa.
Alkuluku on kokonaisluku (2 tai suurempi), joka on jaollinen vain 1:llä ja itsellään.
Esimerkiksi luku 7 on alkuluku, koska se ei ole jaollinen luvuilla 2–6. Samoin luku 11 on alkuluku, koska se ei ole jaollinen luvuilla 2–10.
Luku 10 ei ole alkuluku, koska se on jaollinen 2:lla ja 5:llä. Myöskään luku 75 ei ole alkuluku, koska se on jaollinen 3:lla, 5:llä, 15:llä ja 25:lla.
Tee ohjelma, joka tarkistaa, onko käyttäjän antama kokonaisluku alkuluku.
Anna luku: 7 Luku on alkuluku.
Anna luku: 10 Luku ei ole alkuluku.
Tee ohjelma, joka tulostaa ensimmäiset alkuluvut käyttäjän syötteen mukaisesti.
Kuinka monta? 5 2 3 5 7 11
Kuinka monta? 10 2 3 5 7 11 13 17 19 23 29
Tee ohjelma, joka tulostaa tiettyä lukua pienemmät alkuluvut käyttäjän syötteen mukaisesti.
Yläraja? 10 2 3 5 7
Yläraja? 20 2 3 5 7 11 13 17 19
Tee ohjelma, joka laskee, kuinka monta alkulukua on välillä 1–1000000.
Vihje: Erastotheneen seulasta voi olla hyötyä.
Et kai kirjoittanut edellisten tehtävien koodia suoraan mainiin? Entä jos tarvitsisit alkulukutunnistusta jossain muussa ohjelmassa?
Alkulukupalvelu kannattaakin eristää omaksi luokaksi. Näin alkulukupalvelu on helppo ottaa käyttöön jossain muussa ohjelmassa.
Tee luokka Alkulukupalvelu
jota voi käyttää seuraavaan tapaan:
public static void main(String[] args) { Alkulukupalvelu alkuluvut = new Alkulukupalvelu(10000); if ( alkuluvut.onkoAlkuluku(97) ) System.out.println("luku 97 on alkuluku"); else System.out.println("luku 97 ei ole alkuluku"); System.out.println("37 ensimmäistä alkulukua:"); for ( int luku : alkuluvut.ensimmaisetAlkuluvut(37) ) { System.out.println(luku); } System.out.println("alkuluvut väliltä 2-10000"); for ( int luku : alkuluvut.alkuluvutAsti(10000) ) { System.out.println(luku); } }
Metodit siis ovat
public boolean onkoAlkuluku(int luku)
public ArrayList<Integer> alkuluvutAsti(int n)
public ArrayList<Integer> ensimmaisetAlkukuvut(int n)
Konstruktorissa kerrotaan isoin luku joonka palvelu pystyy käsittelemään. Eli jos on luotu
alkuluvut = new Alkulukupalvelu(10000)
, esim. metodin onAlkuluku
ei tarvitse toimia jos parametri on suurempi kuin 10000. Myös muut metodit toimivat ainoastaan alkulukupalvelun alustuksen yhteydessä annetuun rajaan asti.
Pyri tekemään Alkulukupalvelun metodeista mahdollisimman selkeitä. Käytä tarvittaessa olion sisäisiä apumetodeja selkeyttämään koodia. Apumetodit kannattaa määritellä private
-näkyvyysmääreellä, jolloin ne eivät näy olion ulkopuolelle.
Seuraava tehtävä on varsin haastava. Ohjelman rakenne kannattaa suunnitella kynän ja paperin avulla ennen koodaamista!
Tee ohjelma, joka tulostaa kirjainneliön seuraavien esimerkkien mukaisesti. Voit olettaa, että kerrosten määrä on 1–26.
Kerrokset: 2 AAA ABA AAA
Kerrokset: 3 AAAAA ABBBA ABCBA ABBBA AAAAA
Kerrokset: 4 AAAAAAA ABBBBBA ABCCCBA ABCDCBA ABCCCBA ABBBBBA AAAAAAA