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.
Eduskuntavaalit ovat jo ovella ja nukkuvien puolue on huolestuttavan suuri. Tässä tehtävässä luodaan vaalikone, joka äänestäjiä löytämään itselleen sopivan ehdokkaan.
Ohjelmoi luokka EhdokasValitsin
, jolla on
metodi public String annaEhdokas()
.
Ehdokkaiden valintaan saat käyttää mitä menetelmää tahansa, esim. arpomalla nimen luokan sisällä olevalta ehdokaslistalta. Luokkaa voisi käyttää
esimerkiksi seuraavalla tavalla:
System.out.println(new EhdokasValitsin().annaEhdokas());
Ohjelma voisi tulostaa esimerkiksi:
Aku Ankka
Huomaa, että sinun ei ole välttämätöntä määritellä luokallesi konstruktoria. Yleisesti ottaen konstruktorin voi jättää luomatta, jos luokan ilmentymät ovat sellaisenaan valmiita käyttöön.
Tee luokka Vaalikone
periyttämällä se Swingin JFrame
-luokasta.
Luotaessa luokan ilmentymä, aukeaa ohjelman käyttäjälle tyhjä ikkuna, jonka
nimi on "Vaalikone" (näkyy yläpalkissa). Tutustu JFramen
API-kuvaukseen, jotta saat ohjelman nimen liitettyä ikkunaan. Ota mallia myös materiaalin luvusta 20.2.
Käyttöliittymän käynnistäväksi pääohjelmaksi riittää
public static void main(String[] args) { new Vaalikone(); }
Ohjelmasi tulisi näyttää suunnilleen seuraavalta:
Lisää käyttöliittymään (Vaalikone
-luokkaan) yksi
JLabel
-tekstielementti ja JButton
-nappula,
jossa lukee "Anna ehdokas". Tekstielementissä oletusarvoisesti
näytettäväksi tekstiksi voit asettaa mitä tahdot (laita kuitenkin jotain,
jotta havaitset elementin).
Ohjelmasi tulisi näyttää suunnilleen seuraavalta.
JFrame
-luokan metodi pack()
pakkaa ikkunan automaattisesti
sopivan kokoiseksi (oikeastaan tämä metodi tulee aina luokasta
java.awt.Window
asti).
Lisää Vaalikone
-luokkaan tehtävän ensimmäisessä
osassa tekemäsi EhdokasValitsin
-luokan ilmentymä.
Tee ohjelmasta sellainen, että nappulaa painamalla tekstikenttään
näytetään ehdokasvalitsimen annaEhdokas()
-metodin
palauttama merkkijono.
Tämä onnistuu laittamalla Vaalikone
toteuttamaan
ActionListener
-rajapinta sopivalla tavalla.
Ohjelma voisi näyttää seuraavalta kun nappia painellaan.
Tässä tehtäväsarjassa toteutetaan seuraavan tapainen peli:
Pelin pääluokka on Nopeustesti
, jonka koodi on seuraava:
import java.awt.*; import javax.swing.*; public class Nopeustesti { public static void main(String[] args) { JFrame ikkuna = new JFrame(); ikkuna.setSize(440, 240); ikkuna.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); ikkuna.setVisible(true); Peli peli = new Peli(); peli.setBackground(Color.BLACK); Container sisalto = ikkuna.getContentPane(); sisalto.add(peli); } }
Varsinainen peli on luokassa Peli
,
joka toteuttaa luokan JPanel
.
Luokan pohja on seuraava:
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Peli extends JPanel { public void paint(Graphics g) { super.paint(g); // tähän tulee piirtokoodia } }
Lisää metodiin paint
koodi,
joka piirtää testiksi pelin käyttöliittymän.
Tähän kuuluvat neljä tummanharmaata ympyrää
sekä tekstit pistemäärää ja näppäinohjetta varten.
Käyttöliittymän tulisi näyttää suunnilleen tältä:
Tällä komennolla voit piirtää tekstiä:
g.drawString("F2: Uusi peli", 100, 100);
Näillä komennoilla voit vaihtaa piirtoväriä ennen piirtämistä:
g.setColor(Color.DARK_GRAY);
g.setColor(Color.WHITE);
Pelin aikana yksi neljästä napista on sytytetty. Valitse sytytetyille napeille sopivat kirkkaat värit ja lisää peliin metodi, joka sytyttää satunnaisen napin. Lisää pelin alkuun komento, joka sytyttää satunnaisen napin.
Seuraavassa kuvassa toinen nappi on sytytetty:
Liitä peliin näppäimistön käsittely
materiaalin ohjeen mukaisesti. Muista lisätä
metodin määrittelyyn maininta
rajapinnan KeyListener
toteuttamisesta.
Myös pääohjelman main
-metodin
loppuun tarvitaan seuraava lisäys:
ikkuna.addKeyListener(peli);
Lisää metodiin keyPressed
testikoodia,
jolla voit tutkia, mitä näppäimiä käyttäjä painaa.
Saat näppäinkoodin selville seuraavalla komennolla:
int koodi = e.getKeyCode();
Voit vaikkapa tulostaa näppäinkoodin
tutulla System.out.println
-metodilla.
Voit myös testata näppäimistön käsittelyä
sytyttämällä satunnaisen napin,
kun käyttäjä painaa jotain näppäintä.
Peliin tarvitaan ajastin, joka sytyttää uuden satunnaisen napin tietyn ajan kuluttua.
Voit tehdä ajastimesta seuraavan luokan:
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.Timer; public class Ajastin extends Timer implements ActionListener { private Peli peli; public Ajastin(Peli peli, int aika) { super(aika, null); this.peli = peli; this.addActionListener(this); this.setRepeats(false); } public void actionPerformed(ActionEvent e) { peli.aikaOnKulunut(); } }
Ajastin käynnistetään näin Peli
-luokassa:
Ajastin ajastin = new Ajastin(this, 1000); ajastin.start();
Ajastimelle annetaan odotettava aika millisekunneissa
(1000 millisekuntia on 1 sekunti).
Kun aika on kulunut loppuun, ajastin kutsuu luokassa
Peli
olevaa metodia aikaOnKulunut
.
Muuta peliä niin, että sytytetty nappi vaihtuu sekunnin välein ajastimen avulla. Toimiihan arvontasi niin, että samaa nappia ei sytytetä kaksi kertaa peräkkäin (jolloin pelaaja ei huomaisi vaihtumista)?
Huom! Älä kutsu ajastinta paint
-metodissa!
Muuta peliä niin, että se kirjaa muistiin pelaajan painaman napin. Sitten kun ajastimen aika on päättynyt, pelin tulisi tarkistaa, onko pelaaja painanut oikeasta napista. Jos nappi on oikea, seuraava nappi syttyy, jos nappi on väärä, peli päättyy.
Peli olisi liian helppo, jos nappi vaihtuisi aina sekunnin välein. Muuta peliä niin, että vaihtumistahti kiihtyy jatkuvasti, kunnes saavutetaan tietty raja. Hyvä ratkaisu voisi olla aloittaa 1000 ms:stä ja pienentää vaihtumisväliä 10 ms:llä, kunnes vaihtumisväli on 200 ms.
Lisää peliin vielä pisteenlasku ja varmista,
että pelin aloittaminen (F2) ja lopettaminen (Esc)
toimivat oikealla tavalla. Saat ohjelman lopettamaan kutsumalla metodia System.exit(0);
parametrin arvo voi kutsussa olla mikä tahansa, yleensä käytetään arvoa 0.
Peliin sopisi vielä jonkinlainen graafinen efekti kohtaan, jossa pelaaja tekee virheen ja peli päättyy. Luonteva ratkaisu on vilkuttaa nappeja sopivalla tavalla pelin päättymisen merkiksi.
Peli kaipaa vielä ennätyslistaa, jonka toteutukseksi
sopii viime viikolla tehty luokka Ennatyslista
.
Luokkaan olisi ehkä hyvä lisätä metodi,
jolta voi kysyä, pääseekö tulos listalle.
Seuraavalla koodilla voit näyttää viestejä käyttäjälle:
JOptionPane.showMessageDialog(this, "Ennätyslista:\nAAA 100\nBBB 50\nCCC 20");
Seuraavalla koodilla voit kysyä käyttäjän nimeä:
String nimi = JOptionPane.showInputDialog("Anna nimesi:");
Pino on kaikille ihmisille tuttu asia. Esim. Unicafessa lautaset ovat pinossa. Pinon päältä voi ottaa lautasen ja pinon päälle voi lisätä lautasia. On myös helppo selvittää onko pinossa vielä lautasia jäljellä.
Pino on myös ohjelmoinnissa usein käytetty aputietorakenne. Toteutetaan seuraavassa luokka OmaPino
. Pinon talletetaan lukuja. Pinoon mahtuvien lukujen määrä annetaan pinon konstruktorissa. Pino toteuttaa seuraavan rajapinnan:
public interface Pino { boolean tyhja(); boolean taynna(); void pinoon(int luku); int pinosta(); int huipulla(); int lukuja(); }
Metodien on tarkoitus toimia seuraavasti:
public boolean tyhja()
palauttaa true jos pino on tyhjäpublic boolean taynna()
palauttaa true jos pino on täynnäpublic void pinoon(int luku)
laittaa parametrina olevan luvun pinon päällepublic int huipulla()
kertoo pinon huipulla olevan alkionpublic int pinosta()
poistaa ja palauttaa pinon päällä olevan alkionpublic int lukuja()
kertoo pinossa olevien lukujen määränpublic int tilaa()
kertoo pinon vapaan tilan määränToteutamme pinon hieman aiempaa poikkeavasti. Emme tee alussa pääohjelmaa ollenkaan, vaan valistuneiden atk-ammattilaisten tapaan käytämme pääohjelman sijasta automatisoituja JUnit-testejä. Periaate ei ole vaikea kun perusteet selviävät. Aloitetaan.
Käytämmä tällä kertaa valmiina olevaa projektia. Lataa zip:iksi pakattu projekti täältä. Pura pakkaus sopivaan kohtaan ja avaa projekti NetBeansilla. Kysy pajaohjaajan tai vieressä istuvan apua jos operaatio ei onnistu.
Projekti näyttää seuraavalta:
Sovelluksen koodi (Main, OmaPino ja rajapinta Pino) ovat tavalliseen tapaan Source package:n alla. Uutta on Test package:sta löytyvä testikoodi. Tutkitaan sitä tarkemmin.
import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; public class PinoTest { Pino pino; @Before public void setUp() { pino = new OmaPino(3); } @Test public void alussaTyhja() { assertTrue(pino.tyhja()); } @Test public void lisayksenJalkeenEiTyhja() { pino.pinoon(5); assertFalse(pino.tyhja()); } @Test public void lisattyAlkioTuleePinosta() { pino.pinoon(3); assertEquals(3, pino.pinosta()); } @Test public void lisayksenJaPoistonJalkeenPinoOnTaasTyhja() { pino.pinoon(3); pino.pinosta(); assertTrue(pino.tyhja()); } @Test public void lisatytAlkiotTulevatPinostaOikeassaJarjestyksessa() { pino.pinoon(1); pino.pinoon(2); pino.pinoon(3); assertEquals( 3, pino.pinosta() ); assertEquals( 2, pino.pinosta() ); assertEquals( 1, pino.pinosta() ); } @Test public void tyhjennyksenJalkeenPinoonLaitettuAlkioTuleeUlosPinosta() { pino.pinoon(1); pino.pinosta(); pino.pinoon(5); assertEquals( 5, pino.pinosta() ); } // ... }
Testikoodi koostuu pienistä metodeista joiden edessä on merkintä @Test. Tämän lisäksi löytyy metodi setUp joka ainoastaan luo pino-olion.
setUp on metodi joka suoritetaan ennen jokaista testiä. Jokainen testi siis alkaa tilanteesta, jossa on luotu uusi tyhjä pino. Jokainen yksittäinen @Test-merkitty metodi on oma testinsä. Jokainen testi testaa yhtä pientä osaa pinon toiminnallisuudesta. Testit suoritetaan toisistaan täysin riippumattomina, eli jokainen testi alkaa "puhtaaltä pöydältä", setUp:in alustamasta tilanteesta.
Yksittäiset testit noudattavat samaa kaavaa. Ensin saadaan aikaan tilanne josta varsinainen testattava asia alkaa. Sitten tehdään testattava toimenpide, esim. metodikutsu. Lopuksi tarkastetaan onko tilanne odotetun kaltainen. Jokainen testi on nimetty mahdollisimman kuvaavalla tavalla. Esim:
@Test public void lisayksenJaPoistonJalkeenPinoOnTaasTyhja() { pino.pinoon(3); pino.pinosta(); assertTrue(pino.tyhja()); }
Testataan toimiiko metodi tyhja()
jos pino on tyhjennetty. Eli ensin laitetaan pinoon luku ja tyhjennetään pino kutsumalla metodia pinosta()
. Eli on saatettu aikaan tilanne jossa pino on tyhjennetty. Viimeisellä rivillä testataan, assertEquals()
-testausmetodilla, että pinon metodi tyhja()
palauttaa true. Jos metodin palauttama arvo ei ole odotettu, ilmoittaa NetBeans tilanteesta kuten yo. kuvasta näkyy.
Jokainen testi päättyy jonkun assert
-metodin kutsuun. Esim. assertEquals()
:illa voidaan varmistaa onko metodin palauttama luku tai merkkijono haluttu.
Suorita nyt testit joko painamalla alt ja F6 tai valitsemalla Run -> Test project.
Toteutetaan OmaPino
siten, että pinottavat luvut talletetaan pinon oliomuuttujana olevaan taulukkoon.
Pinon sisällä olevaa taulukkoa kannattaa käyttää seuraavan kuvasarjan osoittamalla tavalla:
p = new OmaPino(4); 0 1 2 3 ----------------- | | | | | ----------------- alkioita: 0 p.pinoon(5) 0 1 2 3 ----------------- | 5 | | | | ----------------- alkiota: 1 p.pinoon(3) 0 1 2 3 ----------------- | 5 | 3 | | | ----------------- alkiota: 2 p.pinoon(7) 0 1 2 3 ----------------- | 5 | 3 | 7 | | ----------------- alkiota: 3 p.pinosta() 0 1 2 3 ----------------- | 5 | 3 | | | ----------------- alkiota: 2
Eli muistetaan kuinka monta alkiota pinossa on. Uusi alkio laitetaan jo pinossa olevien perään. Alkion poisto aiheuttaa sen, että taulukon viimeinen käytössä ollut paikka vapautuu ja alkiomäärän muistavan muuttujan arvo pienenee.
Tehdään pinoa vähän kerrallaan siten, että lopulta kaikki testit toimivat.
Aloitetaan hyvin varovasti. Laita ensin ensimmäinen testi alussaTyhja toimimaan. Älä tee mitään kovin monimutkaista, "quick and dirty"-ratkaisu kelpaa näin alkuun. Kun testi menee läpi (eli näyttää vihreää), siirry seuraavaan kohtaan.
Jatka toteutusta siihen pisteesee, että ensimmäiset 2 testiä menevät läpi.
Laajenna toteutustasi niin, että kolmas ja neljäs testi lisattyTuleeUlosPinosta ja lisayksenJaPoistonJalkeenPinoOnTaasTyhja toimivat.
Eli laajenna toteutustasi siten, että kaikki testit toimivat, eli tuloksena on jokaisen ohjelmoijan unelma "green bar".
Tee testit metodille lukuja()
. Mieti mitä kaikkea pitää testata, että testit ovat kattavat eli kaikki tärkeät sovellustilanteet tulee testatuksi. Kirjoita jokaiselle tilanteelle oma testi.
Toteuta metodi.
Tee testit metodille huipulla()
. Mieti mitä kaikkea pitää testata, että testit ovat kattavat eli kaikki tärkeät sovellustilanteet tulee testatuksi. Kirjoita jokaiselle tilanteelle oma testi.
Toteuta metodi.
Tee luokka ArrayListPino
, joka toteuttaa rajapinnan Pino
, mutta käyttää sisäisesti taulukon sijasta ArrayList:iä.
Koska ArrayList:illä ei ole enimmäiskokoa, on myös ArrayListPino:n koko rajoittamaton.
Kokeile että testit menevät läpi, jos setUp:issa luodaan OmaPino:n sijasta ArrayListPino. Koska ArrayListPino ei täyty koskaan, kommentoi testi public void kunPinoTaynnaIlmoitetaanSenOlevanTaysi()
pois.
Järkevämpää olisi toki luoda oma testi ArrayListPino:a varten. Voit tehdä näin valitsemalla New -> other -> JUnit test -> versio 4.
Tavoitteenamme on tehdä ohjelma jonka avulla voidaan laskea käyttäjän antaman laskutoimituksen arvo. Laskutoimitus voi sisältää nollaa suurempia kokonaislukuja, operaatioita + ja * sekä sulkuja. Laskutoimitus voi siis olla esim. 1 + 2 tai monimutkaisempi, kuten (2 + 3) * (4 + 6) tai sisältää myös sisäkkäisiä sulkuja.
Kun laskutoimitukset kirjoitetaan normaaliin tapaan 1 + 2 eli ns. infixmuodossa missä operaattori on lukujen keskellä, ei laskutoimituksen tekeminen ole täysin suoraviivaista sillä on huomioitava esim. eri operaattorien laskujärjestys.
Jos laskutoimitus muutetaan ensin ns. postfix-muotoon, eli muotoon missä operaattori tulee operandien (eli operoitavien arvojen) jälkeen, on laskutoimituksen teko suoraviivaista.
Esim. 1 + 2 on postfix-muodossa 1 2 + ja (2 + 3) * (4 + 6) on postfix-muodossa 2 3 + 4 6 + *. Emme nyt opettele itse tekemään infix-muodosta postfix-muotoa, vaan käytämme valmista muunninta.
Muunnin löytyy jar-pakettina täältä. Lataa muunnin koneellesi sopivaan hakemistoon, esim. tehtävän projektin alle. Jar-paketit ovat Javassa yleinen tapa valmiiden ohjelmien tai kirjastorutiinien levittämiseen.
Liitä muunnin projektiisi klikkaamalla vasemmalta projektinäkymästä libraries:in kohdalla oikeaa hiiren nappia ja valitsemalla add jar. Jos operaatio ei meinaa onnistua, kysy ohjaajalta tai vieressä istuvalta neuvoa.
Muunninta käytetään seuraavaan tyyliin (huomaa import):
import java.util.Scanner; import muunnin.InfixToPostfix; public class Main { public static Scanner lukija = new Scanner(System.in); public static void main(String[] args) { System.out.print("lasku: "); String lauseke = lukija.nextLine(); InfixToPostfix muuntaja = new InfixToPostfix(lauseke); for (String merkki : muuntaja.muunna() ) { System.out.print( merkki + " " ); } }
Muuntimen konstruktori ottaa laskun merkkijonomuotoisena. Metodi
muunna
palauttaa listan merkkijonoja, jotka muodostavat laskun
postfix-muodon. Esim. jos syöte on "1+2", palauttaa metodi listan jonka
sisältönä ovat merkkijonot "1", "2", "+".
Tee luokka Laskin
, ja sille metodi public static
void laske(String lasku)
joka saa parametriksi laskun
merkkijonomuodossa. Aluksi metodi ei vielä laske laskun tulosta vaan
pelkästään tulostaa laskun infix-muodossa
käyttäen InfixToPostfix
-olioa kuten yllä olevassa
esimerkissä.
HUOM: tee tämä ohjelma pinon kanssa samaan projektiin sillä pinoa tarvitaan pian.
Testaa luokkaa seuraavan pääohjelman avulla:
public static void main(String[] args) { while( true ) { System.out.print("lasku: "); String lasku = lukija.nextLine(); if ( lasku.equals("") ) break; Laskin.laske( lasku ); } }
Ohjelman tulisi toimia seuraavaan tyyliin:
lasku: 1+2 1 2 + lasku: 1*2+3 1 2 * 3 + lasku: (1+2)*3 1 2 + 3 *
Äskeinen versio metodista laske
on vasta välivaihe. Kun olet varmistunut että metodi tulostaa laskun oikein postfix-muodossa, poista tulostus ja muuta metodin tyyppiä siten, että se palauttaa kokonaisluvun. Laita aluksi metodin loppuun return -1;
.
Ennenkuin ohjelmoimme luokan, teemme sille joukon testejä.
Testejä varten tehdään oma testiluokka. Valitse projektin kohdalta new -> Junit test tai new -> other -> junit -> JUnit test. Anna nimeksi esim. LaskinTest
Testiluokassa on hieman ylimääräistä tavaraa. Voit korjava sen siällön seuraavalla:
import org.junit.Test; import static org.junit.Assert.*; public class LaskinTest { @Test public void yksiPlusKaksi(){ assertEquals( 3, Laskin.laske("1+2") ); } }
Mukana on siis valmiina yksi testi, joka tarkastaa, että 1 + 2 todellakin on 3.
Tee testejä lisää. Pyri tekemään testeistä sellaisia, että ne testaavat olellisia, asioita, esim. laskujärjestystä, sulutuksen toimivuutta. Sisäkkäisiä sulkuja.
Kun olet saanut aikaan kattavan joukon testejä, on aika ohjelmoida metodi valmiiksi.
Periaate laskutoimituksen suorittamiseen on seuraava:
Eli toteuta algoritmi metodiisi, käytä edellisen tehtävän
pinoa. Muista, että merkkijonomuodossa olevan luvun saa muutettua
int:iksi metodilla Integer.parseInt( merkkijono )
.
Varmista, että kaikki testit menevät läpi.
Muuta vielä pääohjelmasi seuraavaan muotoon, ja testaa ohjelmaasi manuaalisesti.
public static void main(String[] args) { while( true ) { System.out.print("lasku: "); String lasku = lukija.nextLine(); if ( lasku.equals("") ) break; System.out.println( Laskin.laske( lasku ) ); } }
Ohjelma kaatuu tylysti poikkeukseen, jos syötteen muoto on virheellinen. Testaa ohjelmaa erilaisilla virheellisillä syötteillä ja kokeile mitä poikkeuksia on mahdollsita saada aikaan.
Hoida poikkeukset siten, että laske
-metodi sieppaa kaikki aiheutuneet poikkeukset ja heittää poikkeustilanteessa kutsujalleen poikkeuksen IllegalArgumentException
. Varaudu poikkeukseen pääohjelmassa ja tulosta poikkeuksen tullessa käyttäjälle virheilmoitus.
Javan Scanner-luokka on melko hyvä väline syötteen lukemiseen. Toisinaan olisi kuitenkin tarvetta hieman erilaiselle toiminnallisuudelle. Toteutetaan nyt oma versio syötteenlukijasta. Oma versio, MunLukija
käyttää käytä syötteen lukemiseen Scannerin nextLine()
-metodia, mutta tarjoaa kutsujalle hieman mukavamman käyttörajapinnan kun Javan Scanner.
Tee projekti normaaliin tapaan.
Sijoitamme MunLukija
-luokan omaan pakkaukseen. Luo projektiin pakkaus valitsemalla vasemmalta projektinäkymästä oikealla napilla new -> java package, anna pakkaukselle sopiva nimi, esim. lukija. Pakkausten nimet on tapana kirjoittaa pienellä alkukirjaimella.. Luo luokka MunLukija
pakkauksen sisään.
Varmista, että tulos näyttää seuraavalta:
Eli ohjelmassa on nyt "oletuspakkauksen" (eli olemattoman pakkauksen) lisäksi pakkaus lukija. Main
sijaitsee oletuspakkauksessa ja MunLukija
pakkauksessa lukija. Huomaa, että MunLukija
:n ylimpänä rivinä ilmoitenaan pakkaus jossa koodi sijaitsee. Näin täytyy tehdä kaikille muualla kuin oletuspakkauksessa sijaitseville luokille. NetBeans lisää rivin automaattisesti.
Tehdään munlukijalle metodi public static String lueRivi()
. Käytetään testaamiseen seuraavaa pääohjelmaa:
public class Main { public static void main(String[] args) { String luettiin = lukija.MunLukija.lueRivi(); System.out.println( "luettiin: "+ luettiin ); } }
Koska luokka on pakkauksessa, viitataan luokan nimeen muodossa pakkaus.Luokka. Tämä on hiukan kömpelö tapa, joten importoidaan pakkaus, tällöin pakkauksessa oleva koodi on käytössä suoraan:
import lukija.MunLukija; public class Main { public static void main(String[] args) { String luettiin = MunLukija.lueRivi(); System.out.println( "luettiin: "+ luettiin ); } }
Edellä importoitiin lukija-pakkauksesta ainoastaan luokka MunLukija
. Määrittelemällä import lukija.*;
olisi importoitu koko pakkauksen sisältö.
Toteuta nyt metodi. Käytä MunLukija
:n sisällä Scanneria syötteen lukemiseen.
Tee metodista kuormitettu versio, jossa munLukija tulostaa käyttäjälle parametrina annetun kehotteen:
String luettiin = MunLukija.lueRivi("kirjoita tekstiä: "); System.out.println( "luettiin: "+ luettiin );
Toiminta:
kirjoita tekstiä: Java rules! luettiin: Java rules!
Tee edellisestä metodista hieman poikkeava versio public static String lueEiTyhjaRivi
joka ei hyväksy tyhjää syötettä. Jos käyttäjä kuitenkin antaa tyhjän syötteen (pelkkä enter), kysyy metodi syötettä uudelleen.
Testipääohjelma:
String luettiin = MunLukija.lueEiTyhjaRivi("kirjoita tekstiä (tyhjä ei kelpaa): "); System.out.println( "luettiin: "+ luettiin );
Tuloste:
kirjoita tekstiä (tyhjä ei kelpaa): kirjoita tekstiä (tyhjä ei kelpaa): kirjoita tekstiä (tyhjä ei kelpaa): uskotaan luettiin: uskotaan
Huom: pyri tekemään MunLukija
:sta sellainen, että Scannerin metodia nextLine
kutsutaan vain yhdestä kohtaa koodia. Eli jokainen MunLukijan metodi ei kutsu Scannerin nextLineä, kutsu hoidetaan yhdestä paikasta ja MunLukijan metodit kutsuvat sopivasti toisiaan. Esim. lueEiTyhjaRivi
tekee työnsä kutsumalla metodia lueRivi
.
Toteuta luokalle metodi public static int lueLuku
, joka pyytää käyttäjältä kokonaislukua. Metodi ottaa parametrikseen käyttäjälle näytettävän kysymyksen. Jos käyttäjä syöttää arvon, joka ei ole numero, esitetään kysymys uudestaan kunnes käyttäjä syöttää numeron.
Metodi ei saa heittää poikkeusta minkään muotoisella syötteellä. huomaa, että mekiijonon lukemisen pitää toimia luvun lukemisen jälkeen.
Kaksi vihjettä:
lueRivi
ja Integer.parseInt
Testaa seuraavalla:
int luku = MunLukija.lueLuku("syötä kokonaisluku: "); System.out.println( "luku oli: " + luku); String luettiin = MunLukija.lueEiTyhjaRivi("kirjoita tekstiä (tyhjä ei kelpaa): "); System.out.println( "luettiin: "+ luettiin );
Tulos:
syötä kokonaisluku: ads syötä kokonaisluku: -22,1 syötä kokonaisluku: 321 luku oli: 321 kirjoita tekstiä (tyhjä ei kelpaa): toimii! luettiin: toimii!
Tee vielä metodi public static int lueLukuValilta(String viesti, int alku, int loppu)
joka hyväksyy syötteen ainoastaan jos se on annetulla välillä.
Tee metodi siten, että se käyttää mahdollisimman paljon MunLukija
:n olemassaolevia metodeita!
Testaa että metodisi toimii oikein:
int luku = MunLukija.lueLukuValilta("syötä kokonaisluku väliltä 3...7: ", 3, 7); System.out.println( "luku oli: " + luku);
Tulos:
syötä kokonaisluku väliltä 3...7: 42 syötä kokonaisluku väliltä 3...7: -1 syötä kokonaisluku väliltä 3...7: xyz syötä kokonaisluku väliltä 3...7: 3 luku oli: 3
Nyt tehtyä MunLukija
olisi mukava käyttää jatkossa myös muissa ohjelmissa. Luokan voi toki copy-pasteta aina uuteen projektiin, mutta parempikin keino on olemassa. MunLukija
:sta voi tehdä jar-paketin joka voidaan liittää helposti muihin ohjelmiin.
Luodaan nyt jar-paketti. Kommentoi pääohjelma pois. Paina NetBeansin työkalurivillä olevaa vasaraa. Tulostuksesta selviää jar:in sijainti:
To run this application from the command line without Ant, try: java -jar "/home/mluukkai/tira/ohja/viikko5/MunLukija/dist/MunLukija.jar"
Voit kopioida tiedoston minne tahansa, tai jättää sen paikoilleen. Tärkeintä että tiedät uusia ohjelmia tehdessäsi, mistä se löytyy.
Tee nyt uusi projekti ja liitä jar projektin käyttöön klikkaamalla vasemmalta projektinäkymästä libraries:in kohdalla oikeaa hiiren nappia ja valitsemalla add jar. Kysy ohjaajalta neuvoa jos et löydä jar:ia.
MunLukijan käyttö on helppoa, lisää import jokaisen munlukijaa käyttävän luokan yläosaan:
import lukija.MunLukija; public class Main { public static void main(String[] args) { String rivi = MunLukija.lueEiTyhjaRivi("syötä tekstiä "); // ... } }
Tuottavuutesti paranee, tämän jälkeen ei ole enää koskaan tarvetta
kitjoittaa public static Scanner lukija = new
Scanner(System.in);
Sudokutehtävät ovat välillä ihmiselle hyvin vaikeita, mutta tietokone pystyy ratkaisemaan ne sekunnin murto-osassa. Seuraavaksi tehdään ohjelma, joka ratkaisee sudokutehtävän.
Ohjelmalle annetaan sudokuruudukko, jossa puuttuvien lukujen tilalla on kysymysmerkki. Sitten ohjelma tulostaa ruudukon uudestaan niin, että kysymysmerkit on korvattu oikeilla luvuilla.
Ohjelman toiminnan tulisi näyttää tältä:
Anna sudoku: 8?64??12? ?9???1476 ?4??????3 ?5??1?2?? 2?83?57?1 ??4?9??8? 6??????1? 4851???3? ?17??35?2 Ratkaisu: 836457129 592831476 741962853 359718264 268345791 174296385 623574918 485129637 917683542
Suoraviivainen (ja riittävän tehokas) tapa toteuttaa ohjelma on käydä läpi kaikki järkevät mahdollisuudet sijoittaa lukuja kysymysmerkkien kohdalle.
Esimerkiksi jos ohjelma käy ruudukkoa läpi ylhäältä alas ja vasemmalta oikealle, ensimmäisen kysymysmerkin kohdalla tilanne on seuraava:
Niinpä ohjelma haarautuu tutkimaan vaihtoehtoja, joissa kysymysmerkin kohdalle tuleva luku on 3 tai 7:
8?64??12? ?9???1476 ?4??????3 ?5??1?2?? 2?83?57?1 ??4?9??8? 6??????1? 4851???3? ?17??35?2 / \ / \ / \ / \ 8364??12? 8764??12? ?9???1476 ?9???1476 ?4??????3 ?4??????3 ?5??1?2?? ?5??1?2?? 2?83?57?1 2?83?57?1 ??4?9??8? ??4?9??8? 6??????1? 6??????1? 4851???3? 4851???3? ?17??35?2 ?17??35?2
Vastaava haarautuminen tapahtuu kaikissa kohdissa, joissa on kysymysmerkki. Haarautumisen toteuttamiseen soveltuu erittäin hyvin rekursiivinen metodi. Jos ohjelma onnistuu korvaamaan jossain haarassa kaikki kysymysmerkit luvuilla synnyttämättä ristiriitoja ruudukkoon, sudokun ratkaisu on valmis.