Tehtävät
Toisen osan tavoitteet

Tunnet muutamia osaongelmia sekä niihin liittyviä ratkaisumalleja. Tunnet loogiset operaatiot ja, tai ja ei ja osaat käyttää niitä ehto- ja toistolauseissa. Tunnet käsitteet metodi, metodin parametri, metodin palautusarvo ja ohjelman kutsupino. Osaat luoda metodeja ja ymmärrät miten ohjelman suoritus etenee kun metodia kutsutaan ja toisaalta kun metodikutsusta palataan.

Ongelman osia ja niiden ratkaisumalleja

Oppimistavoitteet
  • Huomaat, että ohjelmissa toistuu samankaltaiset osaongelmat kuten syötteen lukeminen tai laskun laskeminen.
  • Tiedät valmiin ratkaisumallin muutamaan osaongelmaan.
  • Harjoittelet osaongelmiin littyvien ratkaisumallien yhdistämistä laajempien ongelmien ratkaisemisessa.

Samat pienet ongelmat (tai "osaongelmat") toistuvat ohjelmissa yhä uudestaan ja uudestaan: "Lue käyttäjältä syötettä", "Laske lukujen summa", "Laske lukujen keskiarvo", "Lue käyttäjältä syötettä kunnes", "Montako lukua käyttäjä on syöttänyt.", jne. Tarkastellaan muutamia tällaisia ongelmia sekä niihin liittyviä ratkaisuja.

Lukemista

Ratkaisumalli käyttäjältä lukemista vaativiin ohjelmointiongelmiin on suoraviivainen. Mikäli ohjelmassa halutaan lukea käyttäjältä syötettä, tulee ohjelmaan luoda syötteen lukemiseen käytettävä Scanner-apuväline. Scanner-apuväline luodaan pääohjelman (eli lauseen public static void main(String[] args) {) jälkeiselle riville. Scanner-apuvälineen käyttö vaatii, että ohjelman määrittelyä (public class) edeltävillä riveillä on lause import java.util.Scanner;, joka tuo Scanner-apuvälineen ohjelman tietoon.

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {
    public static void main(String[] main) {
        // Luodaan lukemiseen käytettävä Scanner-apuväline
        Scanner lukija = new Scanner(System.in);

        // Esimerkkinä eri tyyppisten muuttujien lukemista
        String merkkijono = lukija.nextLine();
        int luku = Integer.valueOf(lukija.nextLine());
        double liukuluku = Double.valueOf(lukija.nextLine());
        boolean totuusarvo = Boolean.valueOf(lukija.nextLine());

    }

}

Laskemista

Ohjelmissa tulee usein laskea asioita kuten lukujen keskiarvoa tai lukujen summaa. Ratkaisumalli tällaisissa ohjelmissa on seuraava.

  1. Määrittele laskemiseen tarvittavat syötteet ja luo niitä varten muuttujat. Ohjelman syötteitä ovat laskemisessa käytettävät arvot. Syötteiden tyypit tunnistaa tyypillisesti ongelma-alueen kuvauksesta.
  2. Selvitä tehtävä laskuoperaatio ja luo laskuoperaation tulokselle muuttuja. Tee ohjelman syötteiden perusteella lasku, jonka arvo asetetaan laskuoperaation tulokselle varattuun muuttujaan. Myös laskuoperaation tuloksen tyypin tunnistaa ongelma-alueen kuvauksesta.
  3. Kun lasku on laskettu, tee jotain laskun tuloksella. Tämä voi olla esimerkiksi laskuoperaation tuloksen tulostaminen, tai vaikkapa keskiarvon laskemisen yhteydessä lukujen summan jakamista lukujen määrällä.

Esimerkiksi ongelman Tee ohjelma, jonka avulla voidaan laskea kahden kokonaisluvun summa. ratkaisumalli on seuraava.

// Määritellään syötteet ja luodaan niitä varten muuttujat
int eka = 1;
int toka = 2;

// Selvitetään laskuoperaatio ja luodaan laskuoperaation tulokselle muuttuja
int summa = eka + toka;

// Tulostetaan laskuoperaation tulos
System.out.println("Lukujen " + eka + " ja " + toka + " summa on " + summa);  

Sekä lukemista että laskemista sisältävä ohjelma yhdistää edelliset ratkaisumallit. Kahden käyttäjältä pyydetyn luvun tulon laskeva ohjelma on seuraavanlainen.

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {
    public static void main(String[] main) {
        // Luodaan lukemiseen käytettävä Scanner-apuväline
        Scanner lukija = new Scanner(System.in);

        // Määritellään syötteet ja luodaan niitä varten muuttujat
        int eka = 1;
        int toka = 2;

        // Luetaan luvut käyttäjältä
        eka = Integer.valueOf(lukija.nextLine());
        toka = Integer.valueOf(lukija.nextLine());

        // Selvitetään laskuoperaatio ja luodaan laskuoperaation tulokselle muuttuja
        int tulo = eka * toka;

        // Tulostetaan laskuoperaation tulos
        System.out.println("Lukujen " + eka + " ja " + toka + " tulo on " + tulo);  

    }
}

Edellä olevassa esimerkissä ohjelma on toteutettu niin, että muuttujat määritellään ensin ja niihin luetaan arvot vasta tämän jälkeen. Muuttujien määrittelyn sekä niiden arvojen lukemisen voi myös yhdistää.

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {
    public static void main(String[] main) {
        // Luodaan lukemiseen käytettävä Scanner-apuväline
        Scanner lukija = new Scanner(System.in);

        // Määritellään syötteet ja luetaan niihin arvot
        int eka = Integer.valueOf(lukija.nextLine());
        int toka = Integer.valueOf(lukija.nextLine());

        // Selvitetään laskuoperaatio ja luodaan laskuoperaation tulokselle muuttuja
        int tulo = eka * toka;

        // Tulostetaan laskuoperaation tulos
        System.out.println("Lukujen " + eka + " ja " + toka + " tulo on " + tulo);  

    }
}

Kirjoita ohjelma, joka lukee käyttäjältä kokonaisluvun ja tulostaa luvun korotettuna toiseen potenssin eli luvun kerrottuna itsellään.

4
16
-3
9
5
25

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Kirjoita ohjelma, joka lukee käyttäjältä kaksi kokonaislukua ja tulostaa lukujen summan neliöjuuren. Ohjelman ei tarvitse käsitellä negatiivisia lukuja. Alla muutamia esimerkkejä.

1
0
1
5
4
3
1
35
6

Vinkki! Saat neliöjuuren laskettua komennolla Math.sqrt seuraavasti:

int luku = 42;
double neliojuuri = Math.sqrt(42);
System.out.println(neliojuuri);

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Jos ... niin ...

Ongelmat sisältävät usein vaihtoehtoista toiminnallisuutta. Tällaisen toteuttamiseen käytetään ehtolauseita. Ehtolause alkaa if-komennosta, jota seuraa suluissa oleva lauseke. Lauseke evaluoituu joko todeksi tai epätodeksi. Mikäli lauseke evaluoituu todeksi, suoritetaan ehtolauseen lohko, joka on rajattuna aaltosuluilla.

// jos ...
if (lauseke) {
    // niin...
}

Ehtolauseiden ketjuttaminen on mahdollista. Tällöin ongelmat ovat muotoa "jos a, niin b; muulloin jos c, niin d; muulloin jos e, niin f; muulloin g". Ketjutus toteutetaan if-komennon lohkoa seuraavasta else if -komennosta, johon liittyy oma lauseke sekä lohko. Lopuksi tulee elsekomento sekä siihen liittyvä lohko.

if (lauseke yksi) { // jos ...
    // toiminnallisuus kun lauseke yksi on totta
} else if (lauseke kaksi) { // muulloin jos 
  // toiminnallisuus kun lauseke kaksi on totta
  // ja lauseke yksi on epätotta
} else {  // muulloin
  // toiminnallisuus muulloin
}

Esimerkiksi ohjelma, joka tulostaa "ok" kun ohjelmassa olevan lukumuuttujan arvo on suurempi kuin 42, mulloin "ei ok", toteutetaan seuraavasti.

int luku = 15;
if (luku > 42) {
    System.out.println("ok");
} else {
    System.out.println("ei ok")
}

Ehtotoiminnallisuutta voi yhdistää myös muiden ratkaisumallien kanssa. Tarkastellaan ongelmaa "Lue käyttäjältä kaksi lukua. Mikäli lukujen summa on yli 100, tulosta käyttäjälle merkkijono liikaa. Mikäli lukujen summa on alle 0, tulosta käyttäjälle merkkijono liian vähän. Muulloin, tulosta käyttäjälle merkkijono ok.". Ohjelma, joka yhdistää lukemisen, laskemisen ja ehtolauseen on annettu alla.

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {
    public static void main(String[] main) {
        // Luodaan lukemiseen käytettävä Scanner-apuväline
        Scanner lukija = new Scanner(System.in);

        // Määritellään syötteet ja luetaan niihin arvot
        int eka = Integer.valueOf(lukija.nextLine());
        int toka = Integer.valueOf(lukija.nextLine());

        // Selvitetään laskuoperaatio ja luodaan laskuoperaation tulokselle muuttuja
        int summa = eka + toka;

        // Tehdään laskuoperaation tuloksella jotain. Tässä
        // toteutetaan ongelman vaativaa ehdollista toimintaa

        if (summa > 100) { // jos summa on yli sata
            System.out.println("liikaa");
        } else if (summa < 0) { // jos summa on yli alle 0
            System.out.println("liian vähän");  
        } else { // muulloin
            System.out.println("ok");
        }
    }
}

Kirjoita ohjelma, joka lukee käyttäjältä kokonaisluvun. Mikäli luku on pienempi kuin 0, ohjelma tulostaa luvun kerrottuna luvulla -1. Muulloin ohjelma tulostaa käyttäjän syöttämän luvun. Alla on muutamia esimerkkejä ohjelman odotetusta toiminnasta.

-3
3
2
2
-5
5

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Kirjoita ohjelma, joka lukee käyttäjältä kaksi kokonaislukua. Mikäli ensimmäinen luku on suurempi kuin toinen luku, ohjelma tulostaa "Luku (ensimmäinen) on suurempi kuin luku (toinen).". Mikäli ensimmäinen luku on pienempi kuin toinen luku, ohjelma tulostaa "Luku (ensimmäinen) on pienempi kuin luku (toinen).". Muulloin ohjelma tulostaa "Luku (ensimmäinen) on yhtä suuri kuin luku (toinen).". Edeltävissä esimerkeissä kohdat (ensimmäinen) ja (toinen) tulee aina korvata syötetyillä luvuilla.

Alla on muutamia esimerkkejä ohjelman odotetusta toiminnasta.

8
4
Luku 8 on suurempi kuin luku 4.
-3
5
Luku -3 on pienempi kuin luku 5.
1
1
Luku 1 on yhtä suuri kuin luku 1.

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Lue käyttäjältä kunnes

Ongelmat, jotka pyytävät lukemaan käyttäjältä syötettä kunnes käyttäjä syöttää tietynlaisen syötteen, ratkeavat toistolauseen avulla. Kohtaa "kunnes" vastaa toistolauseissa ehtolause, joka sisältää toistolauseesta poistumiseen johtavan break-komennon.

Alla oleva malli sisältää "lue käyttäjältä kunnes"-tyyppisten ohjelmien peruspalat. Nämä ovat ikuisesti toistaminen eli while (true), toistolauseen sisällä oleva lukeminen eli tässä String luettu = lukija.nextLine();, ja toistolauseesta poistuminen eli toistolauseen sisällä olevan if-komennon sisältämä break-komento. Alla toistolauseesta poistutaan kun käyttäjä syöttää merkkijonon "lopeta".

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {

    public static void main(String[] args) {
      // Luodaan lukemiseen käytettävä Scanner-apuväline
      Scanner lukija = new Scanner(System.in);

      // luetaan käyttäjältä kunnes käyttäjä syöttää
      // merkkijonon lopeta
      while (true) {
          String luettu = lukija.nextLine();

          if (luettu.equals("lopeta")) {
            break;
          }

          // lukemisen jälkeen tehtävä toiminnallisuus

      }
        
      // toistolauseen jälkeen tehtävä toiminnallisuus

    }
}

Kirjoita ohjelma, joka lukee käyttäjältä syötettä kunnes käyttäjä syöttää merkkijonon "lopeta". Kun käyttäjä syöttää merkkijonon "lopeta", ohjelma tulostaa merkkijonon "hurraa!", jonka jälkeen ohjelman suoritus päättyy. Muulloin, eli kun käyttäjä syöttää jotain muuta kuin merkkijonon "lopeta", ohjelma tulostaa merkkijonon "hip!".

Alla on muutamia esimerkkejä ohjelman odotetusta toiminnasta.

huuda
hip!
huuda
hip!
huuda
hip!
lopeta
hurraa!    
lopeta
hurraa!    
huuda
hip!
lopeta
hurraa!    

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Kuten muissa ratkaisumalleissa, myös muotoa "lue käyttäjältä kunnes" olevien ongelmien ratkaisumallit voi yhdistää muiden ongelmien ratkaisumallien kanssa. Tarkastellaan seuraavaksi hieman laajempaa esimerkkiä, mikä demonstroi tämän.

"Lue käyttäjältä kokonaislukuja kunnes käyttäjä syöttää luvun nolla. Tämän jälkeen ohjelman tulee tulostaa merkkijono optimistista mikäli käyttäjän syöttämien lukujen keskiarvo on suurempi kuin nolla. Mikäli käyttäjän syöttämien lukujen keskiarvo on pienempi kuin nolla, tulee ohjelman tulostaa merkkijono pessimististä. Muulloin ohjelman tulee tulostaa merkkijono neutraalia meininkiä. Lukua nolla ei tule huomioida keskiarvoa laskettaessa."

Yllä kuvatussa tehtävänannossa on monta osaa:

  • lukeminen: ohjelman tulee lukea käyttäjältä kokonaislukuja
  • laskeminen: ohjelman tulee laskea käyttäjän syöttämien lukujen keskiarvo
  • ehdollinen toiminta (jos ... niin ...): ohjelman lopussa tehtävä tulostus riippuu käyttäjän syöttämien lukujen keskiarvosta
  • toistolause (lue käyttäjältä kunnes): ohjelman tulee lukea käyttäjältä syötettä kunnes käyttäjä syöttää luvun nolla

Tarkastellaan näitä osia ensin erikseen.

Kokonaislukujen lukeminen: Kokonaislukujen lukemista varten tarvitaan Scanner-apuväline. Kokonaisluvun lukeminen tapahtuu lukemalla käyttäjältä merkkijono, joka muunnetaan kokonaisluvuksi komennolla Integer.valueOf.

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {

    public static void main(String[] args) {
      // Luodaan lukemiseen käytettävä Scanner-apuväline
      Scanner lukija = new Scanner(System.in);

      int luku = Integer.valueOf(lukija.nextLine());

    }
}

Keskiarvon laskeminen: lukujen keskiarvon laskemiseen tarvitaan lukujen lukumäärä sekä lukujen summa. Kun nämä ovat tiedossa, saadaan keskiarvo laskettua jakamalla summa lukumäärällä. Huomaa, että nollalla jakaminen ei ole matemaattisessa mielessä sallittua, kts. https://fi.wikipedia.org/wiki/Nollalla_jakaminen. Joissakin ohjelmointikielissä tämä on kuitenkin mahdollista. Mikäli ohjelmassa jakaa luvun nollalla, on lopputulos erittäin harvoin toivottu.

int lukuja = 42;
int summa = 1;

double keskiarvo = summa / lukuja;

Ehdollinen toiminta: ehdollista toimintaa tehdään ehtolauseen avulla. Alla on kuvattuna ohjelmaan kuuluva keskiarvoon liittyvä ehdollinen toiminta.

double keskiarvo = 0.0;
if (keskiarvo < 0) {
    System.out.println("pessimististä");
} else if (keskiarvo > 0) {
    System.out.println("optimistista");
} else {
    System.out.println("neutraalia")
}

Lukeminen kunnes: lukeminen "kunnes" tapahtuu toistolauseen avulla. Toistolause sisältää ehtolauseen, jossa poistutaan toistolauseesta kun käyttäjä syöttää halutun luvun.

Scanner lukija = new Scanner(System.in);

while (true) {
    String syote = lukija.nextLine();

    if (syote.equals("lopeta")) {
        break;
    }
}

Ratkaisumallien toiminta on erillisinä paloina selkeää. Aloitetaan ratkaisumallien yhdistäminen. Yhdistetään ensin kokonaisluvun lukeminen ja lukeminen kunnes. Kokonaisluvun lukeminen tapahtuu osana ehtolausetta.

Scanner lukija = new Scanner(System.in);

while (true) {
    int luku = Integer.valueOf(lukija.nextLine());

    if (luku == 0) {
        break;
    }
}

Yhdistetään edelliseen kokonaisuuteen eli kokonaisluvun lukemiseen kunnes käyttäjä syöttää luvun nolla keskiarvon laskeminen. Keskiarvon laskemiseen -- tai laskemiseen liittyvään ratkaisumalliin yleensä -- kuuluu muuttujien määrittely, laskenta, sekä jotain laskun tuloksella tekeminen. Koska keskiarvo lasketaan käyttäjän syöttämistä luvuista ja lukuja lasketaan kunnes käyttäjä syöttää tietyn luvun, tulee lukujen lukumäärän ja summan laskeminen lisätä osaksi toistolausetta.

Scanner lukija = new Scanner(System.in);

// lukujen määrittely
int lukuja = 0;
int summa = 0;

while (true) {
    int luku = Integer.valueOf(lukija.nextLine());

    if (luku == 0) {
        break;
    }

    // summan ja lukujen lukumäärän laskeminen
    lukuja = lukuja + 1;
    summa = summa + luku;
}

// keskiarvon laskeminen
double keskiarvo = summa / lukuja;

Yhdistetään lopulta ohjelmaan vaihtoehtoinen toiminnallisuus, joka tekee asioita käyttäjän syötteen perusteella.

Scanner lukija = new Scanner(System.in);

// lukujen määrittely
int lukuja = 0;
int summa = 0;

while (true) {
    int luku = Integer.valueOf(lukija.nextLine());

    if (luku == 0) {
        break;
    }

    // summan ja lukujen lukumäärän laskeminen
    lukuja = lukuja + 1;
    summa = summa + luku;
}

// keskiarvon laskeminen
double keskiarvo = summa / lukuja;

// vaihtoehtoinen toiminta keskiarvon perusteella
if (keskiarvo < 0) {
    System.out.println("pessimististä");
} else if (keskiarvo > 0) {
    System.out.println("optimistista");
} else {
    System.out.println("neutraalia")
}

On hyvä kysymys, mikäli alkuperäisen ongelman antaja tietää ettei nollalla lukeminen ole sallittua, tai että nollalla jakaminen johtaa mahdollisesti virheeseen. Mikäli voimme olettaa, että ongelman antaja tietää tilanteesta (esim. hän kertoo siitä), ei poikkeustilanteeseen tarvitse varautua. Mikäli taas on mahdollista, että ongelman antaja ei ole ajatellun nollalla jakamista, ohjelmaan on hyvä toteuttaa erillinen toiminnallisuus, joka estää nollalla jakamisen.

Eräs mahdollinen kokonaisvaltainen ratkaisu alkuperäiseen ongelmaan on seuraava.

Scanner lukija = new Scanner(System.in);

// lukujen määrittely
int lukuja = 0;
int summa = 0;

while (true) {
    int luku = Integer.valueOf(lukija.nextLine());

    if (luku == 0) {
        break;
    }

    // summan ja lukujen lukumäärän laskeminen
    lukuja = lukuja + 1;
    summa = summa + luku;
}

if (lukuja == 0) {
    System.out.println("Ei syötteitä.");
} else {
    // keskiarvon laskeminen
    double keskiarvo = summa / lukuja;

    // vaihtoehtoinen toiminta keskiarvon perusteella
    if (keskiarvo < 0) {
        System.out.println("pessimististä");
    } else if (keskiarvo > 0) {
        System.out.println("optimistista");
    } else {
        System.out.println("neutraalia")
    }
}

Kirjoita ohjelma, joka lukee käyttäjältä syötettä kunnes käyttäjä syöttää merkkijonon "loppu". Tämän jälkeen ohjelma tulostaa käyttäjältä luettujen merkkijonojen määrän. Merkkijonojen määrään ei tule ottaa mukaan syötteen loppumista ilmoittavaa merkkijonoa "loppu".

Alla on muutamia esimerkkejä ohjelman toiminnasta.

hei
vaan
kaikki
loppu
3
never
gonna
give
you
up
never
gonna
let
you
down
loppu
10
loppu
0

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Kirjoita ohjelma, joka lukee käyttäjältä kokonaislukuja kunnes käyttäjä syöttää luvun 0. Tämän jälkeen ohjelma tulostaa lukujen summan.

Alla on muutamia esimerkkejä ohjelman toiminnasta.

2
5
1
0
8
-3
4
1
0
2

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Kirjoita ohjelma, joka lukee käyttäjältä kokonaislukuja kunnes käyttäjä syöttää luvun 0. Tämän jälkeen ohjelma tulostaa syötteessä esiintyneiden ykkösten (eli luvun yksi) lukumäärän.

Alla on muutamia esimerkkejä ohjelman toiminnasta.

2
5
1
0
1
-3
4
1
0
1
-1
8
12
0
0
1
1
0
2

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Kirjoita ohjelma, joka lukee käyttäjältä kokonaislukuja kunnes käyttäjä syöttää luvun 0. Tämän jälkeen ohjelma tulostaa syötteessä esiintyneiden ykkösten (eli luvun yksi) osuuden, eli ykkösten lukumäärän jaettuna syötettyjen lukujen lukumäärällä. Syötteen lopettavaa lukua 0 ei tule ottaa huomioon ykkösten osuutta laskettaessa.

Mikäli ohjelmassa ei syötetä yhtäkään kelpoa lukua (eli käyttäjä aloittaa syöttämällä luvun 0), ohjelman tulee tulostaa "ykkösten osuutta ei voida laskea".

Muistathan, että jakolaskua tehdessä osoittaja tai nimittäjä tulee kertoa 1.0:lla, jolloin luku muuttuu liukuluvuksi. Muulloin on mahdollista, että jakolaskun tulos ei ole toivottu.

Alla on muutamia esimerkkejä ohjelman toiminnasta.

2
5
1
6
0
0.25
-3
1
0
0.5
1
1
0
1.0
0
ykkösten osuutta ei voida laskea

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Lue käyttäjältä kunnes ja rajaa

Ongelmat, jotka pyytävät lukemaan käyttäjältä syötettä kunnes käyttäjä syöttää tietynlaisen syötteen, sekä rajaamaan syötteestä käyttöön vain tietynlaiset arvot, ratkeavat myös toistolauseen avulla. Kohtaa "kunnes" vastaa toistolauseissa break-komennon ja rajaamiseen käytetään toistolauseen alkuun siirtymistä kuvaavaa komentoa continue.

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {

    public static void main(String[] args) {
      // Luodaan lukemiseen käytettävä Scanner-apuväline
      Scanner lukija = new Scanner(System.in);

      // lue käyttäjältä kunnes -- toistolause
      while (true) {
          String syote = lukija.nextLine();

          // kunnes 
          if (poistuminen) {
            break;
          }

          // rajausehto
          if (rajaus) {
              continue;
          }

          // lukemisen jälkeen tehtävä toiminnallisuus
      }
        
      // toistolauseen jälkeen tehtävä toiminnallisuus

    }
}

Alla on kuvattuna ohjelma, missä syötteitä luetaan kunnes, jonka lisäksi myös syötteitä rajataan.

Kirjoita ohjelma, joka lukee käyttäjältä kokonaislukuja kunnes käyttäjä syöttää luvun nolla. Tämän jälkeen ohjelma tulostaa niiden lukujen lukumäärän, jotka ovat pienempiä kuin kymmenen mutta suurempia kuin nolla.

Syötteiden lukeminen lopetetaan kokonaislukuun nolla. Rajauksia on kaksi: (1) luvut, jotka ovat suurempia tai yhtä suuria kuin kymmenen tulee rajata pois; (2) luvut, jotka ovat pienempiä tai yhtä suuria kuin nolla tulee rajata pois. Lukumäärän laskemiseen tarvitaan erikseen muuttuja. Ohjelma kokonaisuudessaan alla.

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {

    public static void main(String[] args) {
      // Luodaan lukemiseen käytettävä Scanner-apuväline
      Scanner lukija = new Scanner(System.in);

      int rajattujenLukujenLukumaara = 0;

      // lue käyttäjältä kunnes -- toistolause
      while (true) {
          // lue käyttäjältä kokonaislukuja
          int luku = Integer.valueOf(lukija.nextLine());

          // kunnes käyttäjä syöttää nollan
          if (luku == 0) {
            break;
          }

          // rajataan pois luvut, jotka ovat suurempia 
          /// tai yhtä suuria kuin kymmenen
          if (luku >= 10) {
              continue;
          }

          // rajataan pois luvut, jotka ovat pienempiä 
          // tai yhtä pieniä kuin nolla
          if (luku <= 0) {
              continue;
          }

          // lukemisen jälkeen tehtävä toiminnallisuus
          rajattujenLukujenLukumaara = rajattujenLukujenLukumaara + 1;
      }
        
      // toistolauseen jälkeen tehtävä toiminnallisuus
      System.out.println(rajattujenLukujenLukumaara);

    }
}

Alla muutamia ohjelman esimerkkitulosteita.

11
4
-2
1
0
2
10
0
0

Kirjoita ohjelma, joka lukee käyttäjältä kokonaislukuja kunnes käyttäjä syöttää luvun 0. Tämän jälkeen ohjelma tulostaa syötteessä esiintyneiden positiivisten (eli nollaa suurempien lukujen) keskiarvon.

Mikäli ohjelmassa ei syötetä yhtäkään positiivista lukua, ohjelman tulee tulostaa "keskiarvon laskeminen ei ole mahdollista".

Alla on muutamia esimerkkejä ohjelman toiminnasta.

3
5
1
-3
0
3.0
0
keskiarvon laskeminen ei ole mahdollista
-3
1
0
1.0
1
1
0
1.0

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.
Osaongelmien tunnistaminen ja ratkaiseminen

Tutki ongelman kuvausta tai tehtävänantoa ja tunnista ongelmaan liittyvät avainsanat kuten "lue kunnes" tai "lue kokonaislukuja". Kullekin avainsanalle, tunnista niihin liittyvät ratkaisumallit, ja ota ratkaisumallit käyttöön. Mikäli ongelmaan liittyy aiemmin tuntemattomia osaongelmia, eriytä osaongelma ensin erilleen kaikesta muusta ja harjoittele sen ratkaisemista -- yrittäminen (ja erehtyminen) on tärkeää!. Kun aiemmin tuntemattomat osaongelmat ja niiden ratkaisut alkavat tulla tutuiksi, yhdistä osaongelman ratkaisu osaksi isomman ongelman ratkaisua.

Loogiset operaatiot

Oppimistavoitteet
  • Tunnet loogiset operaatiot ja, tai, sekä ei, ja osaat käyttää niitä osana ehtolauseen lauseketta.
  • Tunnet ehtolauseen suoritusjärjestyksen ja tiedät, että ehtolauseiden läpikäynti lopetetaan ensimmäiseen ehtoon, jonka lauseke evaluoituu todeksi.
  • Osaat käyttää toistolauseen ehtona totuusarvon palauttavaa lauseketta, jolla päätetään jatketaanko toistoa vaiko ei.

Materiaalin esimerkeissä ja tehtävissä käytetyt ehtolauseet ovat tähän mennessä käyttäneet yksinkertaisia lausekkeita, joilla on tarkasteltu ehtolauseeseen ja toistolauseeseen liittyvän lähdekoodin suorittamista. Esim.

if (lauseke) {
    System.out.println("Suoritetaan jos lausekkeen arvo on true");
}
int luku = 2;

if (luku % 2 == 0) {
    System.out.println("Luku on parillinen");
}
Luku on parillinen

Ehtolauseen lauseke voi koostua myös useammasta osasta, joissa käytetään loogisia operaatioita ja &&, tai ||, sekä ei !. Kahdesta lausekkeesta koostuva lauseke, joka yhdistetään ja-operaatiolla, on totta jos ja vain jos yhdistettävistä lausekkeista evaluoituvat todeksi. Kahdesta lausekkeesta koostuva lauseke, joka yhdistetään tai-operaatiolla, on totta jos jompikumpi tai molemmat yhdistettävistä lausekkeista evaluoituvat todeksi. Loogista operaatiota ei käytetään totuusarvon muuntamiseen truesta falseksi tai falsesta trueksi.

Seuraavassa yhdistetään &&:lla eli ja-operaatiolla kaksi yksittäistä ehtoa. Koodilla tarkistetaan, onko muuttujassa oleva luku suurempi kuin 4 ja pienempi kuin 11, eli siis välillä 5-10:

System.out.println("Onkohan luku väliltä 5-10: ");
int luku = 7;

if (luku > 4 && luku < 11) {
    System.out.println("On! :)");
} else {
    System.out.println("Ei ollut :(")
}
Onkohan luku väliltä 5-10:
On! :)

Seuraavassa annetaan ||:n eli tai-operaation avulla kaksi vaihtoehtoa, onko luku pienempi kuin 0 tai suurempi kuin 100. Ehto toteutuu jos luku täyttää jommankumman ehdon:

System.out.println("Onkohan luku pienempi kuin 0 tai suurempi kuin 100");
int luku = 145;

if (luku < 0 || luku > 100) {
    System.out.println("On! :)");
} else {
    System.out.println("Ei ollut :(")
}
Onkohan luku pienempi kuin 0 tai suurempi kuin 100
On! :)

Seuraavassa käännetään ! ei-operaatiolla lausekkeen luku > 4 tulos. Ei-operaatio merkitään lauseketta ennen niin, että käännettävä lauseke rajataan suluilla, ja ei-operaatio lisätään sulkuja ennen.

int luku = 7;

if (!(luku > 4)) {
    System.out.println("Luku _ei_ ole suurempi kuin 4.");
} else {
    System.out.println("Luku on _suurempi tai yhtäsuuri kuin_ 4.")
}
Luku on _suurempi tai yhtäsuuri kuin_ 4.

Alla on kuvattuna lausekkeiden toimintaa kun lausekkeissa on loogisia operaatioita.

luku luku > 0 luku < 10 luku > 0 && luku < 10 !(luku > 0 && luku < 10) luku > 0 || luku < 10
-1 false true false true true
0 false true false true true
1 true true true false true
9 true true true false true
10 true false false true true

Tee ohjelma, joka kysyy käyttäjän iän ja tarkistaa, että se on mahdollinen (ainakin 0 ja korkeintaan 120). Käytä ohjelmassa vain yhtä if-komentoa.

Kuinka vanha olet? 10
OK
Kuinka vanha olet? 55
OK
Kuinka vanha olet? -3
Mahdotonta!
Kuinka vanha olet? 150
Mahdotonta!

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Ehtolauseiden suoritusjärjestys

Tutustutaan klassiseen ohjelmointiongelmaan:

'Kirjoita ohjelma, joka kysyy käyttäjältä lukua yhden ja sadan väliltä ja tulostaa luvun. Jos luku on kolmella jaollinen, luvun sijaan tulostetaan "Fizz". Jos luku on viidellä jaollinen, luvun sijaan tulostetaan "Buzz". Jos luku on sekä kolmella että viidellä jaollinen, luvun sijaan tulostetaan "FizzBuzz"'.

Ohjelmoija lähtee ratkaisemaan tehtävää lukemalla ongelmakuvauksen, ja luomalla ohjelmakoodia ongelmakuvausta seuraten. Koska ohjelman suoritusehdot esitellään ongelmassa annetussa järjestyksessä, muodostuu ohjelman rakenne järjestyksen perusteella. Ohjelman rakenne muodostuu seuraavien askelten perusteella:

  1. Tee ohjelma, joka lukee luvun käyttäjältä ja tulostaa sen.
  2. Jos luku on jaollinen kolmella, tulosta luvun sijaan merkkijono "Fizz".
  3. Jos luku on jaollinen viidellä, tulosta luvun sijaan merkkijono "Buzz".
  4. Jos luku on jaollinen kolmella ja viidellä, tulosta luvun sijan merkkijono "FizzBuzz".

Jos-tyyppiset ehdot on helppo toteuttaa if - else if - else -valintakäskyjen avulla. Alla oleva koodi on toteutettu yllä olevien askelten perusteella, mutta se ei kuitenkaan toimi oikein, kuten alla olevista esimerkeistä huomataan.

Scanner lukija = new Scanner(System.in);

int luku = Integer.valueOf(lukija.nextLine());

if (luku % 3 == 0) {
    System.out.println("Fizz");
} else if (luku % 5 == 0) {
    System.out.println("Buzz");
} else if (luku % 3 == 0 && luku % 5 == 0) {
    System.out.println("FizzBuzz");
} else {
    System.out.println(luku);
}
3
Fizz
4
4
5
Buzz
15
Fizz

Edellisessä lähestymistavassa ongelmana on se, että ehtolauseiden läpikäynti lopetetaan ensimmäiseen ehtoon, jonka arvo on totta. Esimerkiksi luvulla 15 tulostetaan merkkijono "Fizz", sillä luku on kolmella jaollinen (15 % 3 == 0).

Yksi lähestymistapa yllä olevan ajatusketjun kehittämiseen on ensin etsiä vaativin ehto ja toteuttaa se. Tämän jälkeen toteutettaisiin muut ehdot. Yllä olevassa esimerkissä ehto "jos luku on jaollinen kolmella ja viidellä" vaatii kahden tapauksen toteutumista. Nyt ajatusketju olisi muotoa.

  1. Tee ohjelma, joka lukee luvun käyttäjältä.
  2. Jos luku on jaollinen kolmella ja viidellä, tulosta luvun sijan merkkijono "FizzBuzz".
  3. Jos luku on jaollinen kolmella, tulosta luvun sijaan merkkijono "Fizz".
  4. Jos luku on jaollinen viidellä, tulosta luvun sijaan merkkijono "Buzz".
  5. Muulloin ohjelma tulostaa käyttäjältä luetun luvun.

Nyt ongelmakin tuntuu ratkeavan.

Scanner lukija = new Scanner(System.in);

int luku = Integer.valueOf(lukija.nextLine());

if (luku % 3 == 0 && luku % 5 == 0) {
    System.out.println("FizzBuzz");
} else if (luku % 3 == 0) {
    System.out.println("Fizz");
} else if (luku % 5 == 0) {
    System.out.println("Buzz");
} else {
    System.out.println(luku);
}
2
2
5
Buzz
30
FizzBuzz

Vuosi on karkausvuosi, jos se on jaollinen 4:llä. Kuitenkin jos vuosi on jaollinen 100:lla, se on karkausvuosi vain silloin, kun se on jaollinen myös 400:lla.

Tee ohjelma, joka lukee käyttäjältä vuosiluvun, ja tarkistaa, onko vuosi karkausvuosi.

Anna vuosi: 2011
Vuosi ei ole karkausvuosi.
Anna vuosi: 2012
Vuosi on karkausvuosi.
Anna vuosi: 1800
Vuosi ei ole karkausvuosi.
Anna vuosi: 2000
Vuosi on karkausvuosi.

Vihje 1: Jollain luvulla jaollisuuden voi tarkastaa jakojäännösoperaation % avulla seuraavasti.

int luku = 5;

if (luku % 5 == 0) {
    System.out.println("Luku on viidellä jaollinen!");
}

if (luku % 6 != 0) {
    System.out.println("Luku ei ole kuudella jaollinen!")
}
Luku on viidellä jaollinen!
Luku ei ole kuudella jaollinen!

Vihje 2: mieti ongelmaa if, else if, else if, ... -vertailujen ketjuna ja aloita ohjelman rakentaminen tilanteesta, missä voit olla varma, että ohjelma ei ole karkausvuosi.

Scanner lukija = new Scanner(System.in);
int luku = Integer.valueOf(lukija.nextLine());

if (luku % 4 != 0) {
    System.out.println("Vuosi ei ole karkausvuosi.");
} else if (...) {
    ...
} ...

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Toistolauseen ehto

Olemme tähän mennessä käyttäneet toistolausetta, jonka suluissa on totuusarvo true, jolloin toistoa on jatkettu ikuisesti (tai kunnes toistolauseessa päädytään komentoon break). Toistolauseen sulut, joihin olemme tähän mennessä asettaneet aina arvon true sisältävät oikeastaan ehtolausekkeen, aivan samalla tavalla kuin if-komentoa seuraavat sulut. Arvon true voi korvata lausekkeella, joka evaluoidaan ohjelman suorituksen yhteydessä. Lauseke määritellään täsmälleen samalla tavalla kuin ehtolauseen (if) lauseke.

Seuraavassa esimerkissä tulostetaan luvut 1, 2, ..., 5. Kun luku-muuttujan arvo on yli 5, while-ehto ei ole enää voimassa ja toistaminen lopetetaan.

int luku = 1;

while (luku < 6) {
    System.out.println(luku);
    luku++;
}

Lue ylläoleva "niin pitkään kuin muuttujan luku arvo on pienempi kuin 6, tulosta muuttujan luku arvo ja kasvata muuttujan luku arvoa yhdellä".

Yllä muuttujan luku arvoa kasvatetaan yhdellä aina kun toistolauseen lohko suoritetaan.

Alla on video toistolauseen käytöstä.

Kirjoita ohjelma, joka lukee käyttäjältä kokonaisluvun. Tämän jälkeen ohjelma tulostaa luvut nollasta käyttäjän syöttämään lukuun. Voit olettaa, että käyttäjä syöttää aina positiivisen luvun. Alla on muutamia esimerkkejä ohjelman toivotusta toiminnasta.

4
0
1
2
3
4
1
0
1

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Kirjoita ohjelma, joka lukee käyttäjältä kokonaisluvun. Tämän jälkeen ohjelma tulostaa luvusta lähtien luvut sataan asti. Voit olettaa, että käyttäjä syöttää aina luvun, joka on pienempi kuin 100. Alla on muutamia esimerkkejä ohjelman toivotusta toiminnasta.

99
99
100
-4
-4
-3
-2
-1
0
1
2
... (monta lukua välissä) ...
98
99
100

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.
Moniosaisista tehtävistä

Huomaa, että tästä lähtien tehtävissä saattaa olla useampia osia. Jokainen osa lasketaan yksittäiseksi tehtäväksi, eli esimerkiksi seuraava tehtävä vastaa kahta yksiosaista tehtävää. Useampiosaiset tehtävät voi tyypillisesti palauttaa myös vaikka tehtävä ei olisi vielä valmis -- tällöin valmiista osista lisätään pisteet kirjanpitoon.

Tämä tehtävä on ensimmäinen kaksiosainen tehtävä. Kun teet molemmat osat, saat tehtävästä kaksi tehtäväpistettä. Voit palauttaa tehtävän myös siten, että vain ensimmäinen osa on tehtynä.

Mihin asti?

Kirjoita ohjelma, joka tulostaa kokonaisluvut 1:stä käyttäjän antamaan lukuun asti.

Mihin asti? 3
1
2
3
Mihin asti? 5
1
2
3
4
5

Vihje: käyttäjältä lukemasi luku toimii nyt whilen lopetusehdon ylärajana. Muista että Javassa a <= b tarkoittaa a pienempi tai yhtä suuri kuin b.

Mistä lähtien?

Lisää ohjelmaan käyttäjältä kysyttävä alaraja.

Mihin asti? 8
Mistä lähtien? 5
5
6
7
8

Jos tavoite on suurempi kuin lähtökohta ei tulostu mitään:

Mihin asti? 12
Mistä lähtien? 16

Huom! muista että ala- ja yläraja voivat olla myös negatiivisia!


Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Toistolauseen suoritus ei lopu heti kun toistolauseen ehtolauseke voisi evaluoitua todeksi. Toistolauseen ehtolauseke evaluoidaan aina kun saavutaan toistolauseen alkuun, eli (1) kun ohjelman seuraava suoritettava lause on toistolause, ja (2) kun toistolauseeseen liittyvän lohkon sisältämän ohjelmakoodin suoritus on saatu loppuun.

Tarkastellaan seuraavaa toistolausetta.

int luku = 1;

while (luku != 2) {
    System.out.println(luku);
    luku = 2;
    System.out.println(luku);
    luku = 1;
}

Ohjelman tulostus seuraavanlainen:

1
2
1
2
1
2
...

Vaikka muuttujan luku arvo on välillä 2, toistolauseen suoritus ei lopu koskaan.

Toistolauseen ehto tarkistetaan silloin kun toistolauseen toistaminen aloitetaan sekä silloin kun koodin suoritus on päässyt toistolauseen lopettavaan aaltosulkuun asti. Mikäli toistolauseen ehdon lauseke on evaluoituu todeksi eli muotoon true, suoritus jatkuu toistolauseen alusta. Mikäli lauseke evaluoituu epätodeksi eli muotoon false, suoritus siirtyy toistolausetta seuraavaan lauseeseen.

Vaikka muuttujan luku arvo on ylläolevassa toistolauseessa välillä 2, ei se ole koskaan 2 toistolauseen lopussa. Lopussa ehto luku != 2 on aina totta, ja suoritus jatkuu..

Eräs yleinen ongelmatyyppi on "tee jotain tietty määrä kertoja". Näissä ohjelmissa esiintyy toisto, jonka jokaisella toistokerralla tehdään haluttu toiminnallisuus sekä muutetaan kertojen lukumäärää laskevaa laskurimuuttujaa.

Seuraava ohjelma laskee tulon 4*3 hieman kömpelöllä tavalla eli summana 3 + 3 + 3 + 3:

int tulos = 0;

int i = 0;
while (true) {
    tulos += 3; // tarkoittaa samaa kuin tulos = tulos + 3;
    i++;  // tarkoittaa samaa kuin i = i + 1;

    if (i == 4) {
         break;
    }
}

System.out.println(tulos);

Saman toiminnallisuuden voi toteuttaa myös seuraavasti.

int tulos = 0;

int i = 0;
while (i < 4) {
    tulos += 3; // tarkoittaa samaa kuin tulos = tulos + 3;
    i++;  // tarkoittaa samaa kuin i = i + 1;
}

System.out.println(tulos);

Mitä enemmän ohjelmassa on muuttujia, sitä haastavampaa ohjelman askeleittaisen suorituksen seuraaminen on. Ohjelman ymmärtämisen kannalta suorituksen seuraaminen on kuitenkin tärkeää.

Yksi näppärä tapa muuttujien arvojen tarkasteluun toistolauseessa on taulukko. Seuraavaan taulukkoon on kirjoitettu auki edellisen esimerkin muuttujien tulos ja i arvot kullakin toistolauseen ehdon i < 4 vertailuhetkellä.

tulos i i < 4
0 0 true
3 1 true
6 2 true
9 3 true
12 4 false

Toistolauseen suoritus loppuu kun muuttujan summa arvo on 12 ja muuttujan i arvo on 4 (ehto i < 4 on tällöin epätotta).

Tee ohjelma, joka laskee summan 1+2+3+...+n, missä n on käyttäjän syöttämä luku.

Esimerkkitulostuksia:

Mihin asti? 3
Summa on 6

Edellisessä esimerkissä laskettiin 1 + 2 + 3 = 6

Mihin asti? 7
Summa on 28

Ja nyt laskettiin 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28

Vihje: Tee ohjelma while-komennon avulla. Käytä ohjelmassasi apumuuttujaa toistokertojen muistamiseen. Lisää jokaisella toistokerralla toistokerrat muistavan muuttujan arvo apumuuttujaan johon lasket summan arvon.


Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Muuta edellistä tehtävää siten, että käyttäjä määrää summan laskemisen aloituskohdan. Voit olettaa, että käyttäjä antaa ensin pienemmän luvun ja sitten suuremman luvun.

Esimerkkitulostuksia:

Ensimmäinen: 3
Viimeinen: 5
Summa on 12

Edellisessä laskettiin 3 + 4 + 5 = 12

Ensimmäinen: 2
Viimeinen: 8
Summa on 35

Ja nyt laskettiin 2 + 3 + 4 + 5 + 6 + 7 + 8 = 35


Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Tee ohjelma, joka laskee käyttäjän syöttämän luvun kertoman.

Kertoma n! lasketaan kaavalla 1*2*3*...*n. Esimerkiksi luvun 4 kertoma on 24, eli 4! = 1*2*3*4 = 24. Lisäksi on määritelty, että luvun 0 kertoma on 1, eli 0! = 1.

Esimerkkitulostuksia:

Anna luku: 3
Kertoma on 6

Nyt laskettiin 1 * 2 * 3 = 6

Anna luku: 10
Kertoma on 3628800

Ja nyt laskettiin 1 * 2 * 3 * ... * 8 * 9 * 10 = 3628800

Lisätietoa: Kertomaa käytetään erityisesti todennäköisyyslaskennassa tilanteissa, missä halutaan esimerkiksi tarkastella jonkin joukon kaikkia erilaisia järjestyksiä. Esimerkiksi viiden hengen ryhmän voi järjestää 5! erilaiseen jonoon, ja 52 kortin korttipakka voi olla 52! erilaisessa järjestyksessä. Kertomaa voi käyttää myös kombinaatioiden laskemiseen; esimerkiksi 52 kortin korttipakasta on mahdollista jakaa 52! / (5! * (52 - 5)!) erilaisella viiden kortin kättä, ja 40 numeron joukosta voi tehdä yhteensä 40! / (7! * (40 - 7)!) erilaista 7 numeron lottoriviä.


Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.
Ohjelmien tekeminen pienissä paloissa

Kun teet ohjelmaa, oli se sitten harjoitustehtävä tai oma projektisi, mieti minkälaisia osia ohjelma tarvitsee toimiakseen, ja etene näitä pieniä osia yksitellen toteuttaen. Jokaisen osan toteuttamisen jälkeen kokeile tähänastisen ohjelmasi toimintaa.

Älä koskaan yritä ratkaista koko ongelmaa kerralla, sillä tällöin ohjelman suorittaminen ja testaaminen kesken ongelmanratkaisuprosessin on vaikeaa. Aloita jollain helpolla asialla jonka tiedät varmasti osaavasi. Kun yksi ohjelman osa on saatu toimimaan, voit siirtyä ratkaisemaan seuraavaa ongelmaa.

Osa kurssin tehtävistä on valmiiksi osiin pilkottuja. Usein osat pitää vielä pilkkoa ohjelmoinnin kannalta vieläkin pienempiin paloihin. Kannattaa tehdä siten, että suoritat ohjelman lähes jokaisen uuden koodirivin jälkeen. Tällöin varmistat, että ratkaisu on etenemässä haluttuun suuntaan.

Seuraavassa tehtävässä tehdään yksi ohjelma, mutta ohjelman rakentaminen tapahtuu hyvin pienissä paloissa. Tämä on ehdottoman suositeltava tapa aina kun ohjelmoit.

Tehtäväsarja muodostaa yhden isomman ohjelman, jonka toiminnallisuus toteutetaan pienissä paloissa. Jos et tee tehtäväsarjaa loppuun asti, voit lähettää sen tarkastettavaksi vajaatekoisenakin. Tämä onnistuu painamalla testausnapin oikealla puolella olevasta "submit"-napista eli pienestä ylöspäinosoittavasta nuolesta. Vaikka palautusautomaatti valittaakin vielä tekemättä olevien tehtävänosien testeistä, kirjautuvat jo tekemiesi osien pisteet.

Huom: nyt (ja jatkossa) jokainen isomman tehtävän "alitehtävä" on saman arvoinen tehtävä kuin alikohdaton tehtävä. Tämä tehtävä vastaa siis viittä normaalia tehtävää.

Lukujen lukeminen

Tee ohjelma, joka kysyy käyttäjältä lukuja (ohjelma tulostaa käyttäjälle aluksi "Syötä luvut:"), kunnes käyttäjä antaa luvun -1. Kun käyttäjä syöttää luvun -1, ohjelma tulostaa "Kiitos ja näkemiin!" ja päättyy.

Syötä luvut:
5
2
4
-1
Kiitos ja näkemiin!

Lukujen summa

Laajenna edellistä ohjelmaa siten, että ohjelma ilmoittaa käyttäjän syöttämien lukujen summan. (Lukua -1 ei lasketa mukaan.)

Syötä luvut:
5
2
4
-1
Kiitos ja näkemiin!
Summa: 11

Lukujen summa ja lukumäärä

Laajenna edellistä ohjelmaa siten, että ohjelma ilmoittaa myös käyttäjien antamien lukujen lukumäärän. (Lukua -1 ei lasketa mukaan.)

Syötä luvut:
5
2
4
-1
Kiitos ja näkemiin!
Summa: 11
Lukuja: 3

Lukujen keskiarvo

Muuta edellistä ohjelmaa siten, ohjelma ilmoittaa lukujen keskiarvon. (Lukua -1 ei lasketa mukaan.)

Syötä luvut:
5
2
4
-1
Kiitos ja näkemiin!
Summa: 11
Lukuja: 3
Keskiarvo: 3.666666666666

Parilliset ja parittomat

Laajenna edellistä ohjelmaa siten, että ohjelma ilmoittaa parillisten ja parittomien lukujen määrän. (Lukua -1 ei lasketa mukaan.)

Syötä luvut:
5
2
4
-1
Kiitos ja näkemiin!
Summa: 11
Lukuja: 3
Keskiarvo: 3.666666666666
Parillisia: 2
Parittomia: 1

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Tarkastellaan seuraavaksi ohjelmaa, joka lukee käyttäjältä kokonaislukuja. Ohjelma käsittelee negatiiviset luvut epäkelpoina lukuina, positiiviset luvut hyväksyttävinä lukuina, sekä nollan lukemisen lopettamista ilmaisevana lukuna. Kun käyttäjä syöttää nollan, ohjelma tulostaa hyväksyttyjen lukujen summan, hyväksyttyjen lukujen lukumäärän sekä epäkelpojen lukujen lukumäärän.

Alla on kuvattuna eräs mahdollinen ratkaisu, joka ei kuitenkaan ole tyylin kannalta ideaali.

Scanner lukija = new Scanner(System.in);

System.out.print("Anna lukuja, negatiiviset luvut eivät kelpaa: ");
int summa = 0;
int hyvaksytytLuvut = 0;
int epakelvotLuvut = 0;

while (true) {
    int luettu = Integer.valueOf(lukija.nextLine());

    if (luettu == 0) {
        System.out.println("Hyväksyttävien lukujen summa: " + summa);
        System.out.println("Hyväksyttyjä lukuja: " + hyvaksytytLuvut);
        System.out.println("Epäkelvot luvut: " + epakelvotLuvut);
        break;
    }

    if (luettu < 0) {
        epakelvotLuvut++;
        continue;
    }

    summa += luettu;
    hyvaksytytLuvut++;
}

Yllä kuvattu lähestymistapa ei ole suositeltava, sillä se johtaa helposti hyvin monimutkaiseen ohjelman rakenteeseen. Jos toistolauseen lopettamisen yhteydessä pitäisi tehdä muutakin -- esimerkiksi lukea lisää syötteitä -- asetettaisiin kyseinenkin toiminnallisuus helposti ehtolauseen sisälle. Lisätoiminnallisuuden kertyessä, ohjelma muuttuisi yhä vaikeammin ja vaikeammin luettavaksi.

Pitäydytään seuraavassa toistolauseen muodossa:

Scanner lukija = new Scanner(System.in);

// toistolauseessa tarvittavien muuttujien luominen

while (true) {
    // syötteen lukeminen

    // toistolauseesta poistuminen -- break

    // epäkelpojen syötteiden rajaaminen pois -- continue

    // hyväksyttävien syötteiden käsittely
}

// toistolauseesta poistumisen jälkeen suoritettava toiminnallisuus

Toisin sanoen, yllä oleva ohjelma on selkeämpi jos toistolauseesta poistumisen jälkeen tehtävät asiat ovat toistolauseen ulkopuolella.

Scanner lukija = new Scanner(System.in);

System.out.print("Anna lukuja, negatiiviset luvut eivät kelpaa: ");
int summa = 0;
int hyvaksytytLuvut = 0;
int epakelvotLuvut = 0;

while (true) {
    int luettu = Integer.valueOf(lukija.nextLine());

    if (luettu == 0) {
        break;
    }

    if (luettu < 0) {
        epakelvotLuvut++;
        continue;
    }

    summa += luettu;
    hyvaksytytLuvut++;
}

System.out.println("Hyväksyttävien lukujen summa: " + summa);
System.out.println("Hyväksyttyjä lukuja: " + hyvaksytytLuvut);
System.out.println("Epäkelvot luvut: " + epakelvotLuvut);
Ikuinen debaatti: break

Opittavan ja käytettävän toistolauseen ominaisuuksia on tarkasteltu ohjelmoinnin opetukseen liittyvässä tutkimuksessa pitkään. Eräs debaatti liittyy break-komennon käyttöön. Jotkut ovat sitä mieltä, että sen käyttäminen on yleisesti ottaen hyvä käytäntö, toiset taas ovat sitä mieltä, että se toimii esimerkiksi toistolauseen käyttöön tutustumisessa. Jotkut taas eivät suosi sen käyttämistä lainkaan.

Mikäli haluat lukea break-komentiin liittyvästä debaatista lisää, eräs hyvä artikkeli on Break statement considered. Artikkeliin pääsee yliopiston verkosta.

Ohjelman pilkkominen osiin: metodit

Oppimistavoitteet
  • Tunnet käsitteet metodi, metodin parametri, metodin palautusarvo ja ohjelman kutsupino.
  • Osaat luoda metodeja ja osaat kutsua metodeja sekä pääohjelmasta (main-metodi) että muiden metodien sisältä.
  • Osaat luoda parametrillisia ja parametrittomia metodeja, ja osaat luoda metodeja, jotka palauttavat arvon.

Olemme käyttäneet useita erilaisia komentoja Javassa: sijoitusta, laskutoimituksia, vertailuja, if:iä ja whileä. Ruudulle tulostaminen on tehty System.out.println()-lauseella ja lukeminen Integer.valueOf(lukija.nextLine()) lauseella.

Huomaamme, että tulostaminen ja lukeminen poikkeaa if:istä ja while:stä ym. siinä, että komennon perässä on sulut ja joskus sulkujen sisällä komennolle annettava parametrit. "Sulkuihin päättyvät" eivät oikeastaan olekaan komentoja vaan metodeja.

Teknisesti ottaen metodi tarkoittaa nimettyä lauseista koostuvaa joukkoa, eli ohjelman palaa, jota voi kutsua muualta ohjelmakoodista metodille annetun nimen perusteella. Koodirivi System.out.println("olen metodille annettava parametri!") siis tarkoittaa, että kutsutaan metodia, joka suorittaa ruudulle tulostamisen. Metodin sisäinen toteutus -- eli joukko suoritettavia lauseita -- on tässä tapauksessa java-ohjelmointikielen piilottama.

Metodin suorituksen jälkeen palataan takaisin kohtaan, missä ennen metodikutsua oltiin menossa, ja ohjelman suoritus jatkuu tästä. Metodille suluissa annettua syötettä kutsutaan metodin parametriksi -- metodin parametreilla annetaan metodeille tarkempaa tietoa odotetusta suorituksesta; esimerkiksi tulostuslauseelle kerrotaan parametrin avulla mitä pitäisi tulostaa.

Parametrin lisäksi metodilla voi olla paluuarvo. Esim. tuttu lause:

String merkkijono = lukija.nextLine();

sisältää yhden metodikutsun lukija.nextLine. Usein metodin nimeen näyttää liittyvän piste, kuten yllä olevassa lauseessa. Oikeastaan tässäkin metodin nimi on pisteen oikeanpuoleinen osa, eli nextLine(). Pisteen vasemmanpuoleinen osa, eli tässä lukija, kertoo kenen metodista on kyse. Kyseessä on lukijan tarjoama metodi nextLine. Opimme hiukan myöhemmin tarkemmin mistä tässä pisteen vasemmanpuoleisessa osassa on kyse. Tarkka lukija tietysti huomaa, että System.out.println():ssa on "kaksi pistettä". Metodin nimi tässä on println, ja System.out on se kenen metodista on kyse. Karkeasti ottaen System.out tarkoittaa koneen näyttöä.

Vastaavasti lauseessa:

int luku = Integer.valueOf(lukija.nextLine());

on kaksi metodikutsua. Ensin kutsutaan sisempänä olevaa metodia lukija.nextLine, joka palauttaa merkkijonon. Seuraavaksi kutsutaan metodia Integer.valueOf. Metodikutsun Integer.valueOf parametriksi asetetaan se merkkijono, jonka metodin lukija.nextLine kutsu palautti. Metodin Integer.valueOf paluuarvona on merkkijonoa vastaava kokonaisluku.

Tähän mennessä käyttämämme metodit ovat kaikki olleet Javan valmiita metodeita. Opetellaan seuraavaksi tekemään omia metodeita.

Omat metodit

Edellä mainittiin että metodi tarkoittaa nimettyä lauseista koostuvaa joukkoa, jota voi kutsua muualta ohjelmakoodista nimen perusteella. Javan valmiita metodeja on käytetty oikeastaan ensimmäisestä ohjelmasta lähtien.

Javan valmiiden metodien käytön lisäksi ohjelmoija voi kirjoittaa itse metodeja joita sovellus kutsuu. Oikeastaan on hyvin poikkeuksellista jos ohjelmassa ei ole yhtään itse kirjoitettua metodia. Tästä lähtien lähes jokainen kurssilla tehty ohjelma sisältääkin itsekirjoitettuja metodeja.

Ohjelmarunkoon metodit kirjoitetaan main:in aaltosulkeiden ulkopuolelle mutta kuitenkin "uloimmaisten" aaltosulkeiden sisäpuolelle, joko mainin ylä- tai alapuolelle.

import java.util.Scanner;

public class Esimerkki {
    public static void main(String[] args) {
        Scanner lukija = new Scanner(System.in);
        // ohjelmakoodi
    }

    // omia metodeja tänne
}

Tarkastellaan uuden metodin luomista. Luodaan metodi tervehdi.

public static void tervehdi() {
    System.out.println("Terveiset metodimaailmasta!");
}

Ja asetetaan se metodeille kuuluvalle paikalle.

import java.util.Scanner;

public class Esimerkki {
    public static void main(String[] args) {
        Scanner lukija = new Scanner(System.in);
        // ohjelmakoodi
    }

    // omia metodeja tänne
    public static void tervehdi() {
        System.out.println("Terveiset metodimaailmasta!");
    }
}

Metodin määrittely sisältää kaksi osaa. Metodimäärittelyn ensimmäisellä rivillä on metodin nimi eli tervehdi. Nimen vasemmalla puolella tässä vaiheessa määreet public static void. Metodin nimen sisältävän rivin alla on aaltosulkeilla erotettu koodilohko, jonka sisälle kirjoitetaan metodin koodi, eli ne komennot jotka metodia kutsuttaessa suoritetaan. Metodimme tervehdi ei tee muuta kuin kirjoittaa rivillisen tekstiä ruudulle.

import java.util.Scanner; public class Esimerkki { public static void main(String[] args) { Scanner lukija = new Scanner(System.in); // ohjelmakoodi } // omia metodeja tänne public static void tervehdi() { System.out.println("Terveiset metodimaailmasta!"); } }
import java.util.Scanner; public class Esimerkki { public static void main(String[] args) { Scanner lukija = new Scanner(System.in); // ohjelmakoodi } // omia metodeja tänne // MARK }

Itsekirjoitetun metodin kutsu on helppoa, kirjoitetaan metodin nimi ja perään sulut ja puolipiste. Seuraavassa main eli pääohjelma kutsuu tervehdi-metodia yhteensä neljä kertaa.

import java.util.Scanner;

public class OhjelmaRunko {
    public static void main(String[] args) {
        Scanner lukija = new Scanner(System.in);

        // ohjelmakoodi
        System.out.println("Kokeillaan pääsemmekö metodimaailmaan:");
        tervehdi();

        System.out.println("Näyttää siltä, kokeillaan vielä:");
        tervehdi();
        tervehdi();
        tervehdi();
    }

    // omat metodit
    public static void tervehdi() {
        System.out.println("Terveiset metodimaailmasta!");
    }
}

Ohjelman suoritus saa aikaan seuraavan tulosteen:

Kokeillaan pääsemmekö metodimaailmaan:
Terveiset metodimaailmasta!
Näyttää siltä, kokeillaan vielä:
Terveiset metodimaailmasta!
Terveiset metodimaailmasta!
Terveiset metodimaailmasta!

Huomionarvoista tässä on ohjelman suoritusjärjestys. Ohjelman suoritus etenee siten, että pääohjelman -- eli main:in -- rivit suoritetaan ylhäältä alas yksi kerrallaan. Kun lause on metodikutsu, ohjelman suoritus siirtyy metodiin. Metodin lauseet suoritetaan yksi kerrallaan ylhäältä alas. Tämän jälkeen palataan kohtaan, josta metodin kutsu tapahtui. Tarkemmin ottaen metodikutsun jälkeiselle riville.

Jos ollaan tarkkoja niin pääohjelma eli main on itsekin metodi. Kun ohjelma käynnistyy, kutsuu käyttöjärjestelmä main:ia. Metodi main on siis ohjelman käynnistyspiste, jonka ylimmältä riviltä ohjelman suoritus lähtee liikkeelle. Ohjelman suoritus loppuu kun päädytään mainin loppuun.

Tee metodi tulostaTeksti, joka tulostaa tekstin "Alussa olivat suo, kuokka ja Java." sekä rivinvaihdon.

public static void main(String[] args) {
    tulostaTeksti();
}

public static void tulostaTeksti() {
    // kirjoita koodia tähän
}

Ohjelman tulostus:

Alussa olivat suo, kuokka ja Java.

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Laajenna edellistä ohjelmaa siten, että pääohjelma kysyy käyttäjältä, montako kertaa teksti tulostetaan eli montako kertaa metodia kutsutaan.

public static void main(String[] args) {
    // kysy käyttäjältä, montako kertaa teksti tulostetaan
    // kutsu metodia tulostaTeksti while-komennon avulla useita kertoja
}

public static void tulostaTeksti() {
    // kirjoita koodia tähän
}

Ohjelman tulostus:

Kuinka monta?
7
Alussa olivat suo, kuokka ja Java.
Alussa olivat suo, kuokka ja Java.
Alussa olivat suo, kuokka ja Java.
Alussa olivat suo, kuokka ja Java.
Alussa olivat suo, kuokka ja Java.
Alussa olivat suo, kuokka ja Java.
Alussa olivat suo, kuokka ja Java.

Huom: tulosta kehote Kuinka monta? omalle rivilleen!


Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Jatkossa kun esittelemme metodeja, emme erikseen mainitse että niiden täytyy sijaita omalla paikallaan. Metodia ei esimerkiksi voi määritellä toisen metodin sisällä.

Metodien nimennästä

Metodit nimetään siten, että ensimmäinen sana kirjoitetaan pienellä ja loput alkavat isolla alkukirjaimella, tälläisestä kirjoitustavasta käytetään nimitystä camelCase. Tämän lisäksi, metodin sisällä koodi on sisennetty taas neljä merkkiä.

VäärinOikein
public static void Tama_metodi_sanoo_mur ( ) {
System.out.println("mur");
}
public static void tamaMetodiSanooMur() {
    System.out.println("mur");
}

Metodin muuttujat

Muuttujien määrittely metodissa tapahtuu tutulla tavalla. Seuraava metodi laskee parametrina saamiensa lukujen keskiarvon. Keskiarvon laskemisessa käytetään apumuuttujia summa ja ka.

public static double keskiarvo(int luku1, int luku2, int luku3) {

    int summa = luku1 + luku2 + luku3;
    double ka = summa / 3.0;

    return ka;
}

Metodin kutsu voi tapahtua esim seuraavasti

public static void main(String[] args) {
    Scanner lukija = new Scanner(System.in);

    System.out.print("Anna ensimmäinen luku: ");
    int eka = Integer.valueOf(lukija.nextLine());

    System.out.print("Anna toinen luku: ");
    int toka = Integer.valueOf(lukija.nextLine());

    System.out.print("ja kolmas luku: ");
    int kolmas = Integer.valueOf(lukija.nextLine());

    double keskiarvonTulos = keskiarvo(eka, toka, kolmas);

    System.out.print("Lukujen keskiarvo: " + keskiarvonTulos);
}

Huomaa että metodin sisäiset muuttujat summa ja ka eivät näy pääohjelmaan. Tyypillinen ohjelmoinnin harjoittelussa eteen tuleva virhe on yrittää käyttää metodia seuraavasti.

public static void main(String[] args) {
    int eka = 3;
    int toka = 8;
    int kolmas = 4;

    keskiarvo(eka, toka, kolmas);

    // yritetään käyttää metodin sisäistä muuttujaa, EI TOIMI!
    System.out.print("Lukujen keskiarvo: " + ka);
}

Myös seuraavanlaista virhettä näkee usein.

public static void main(String[] args) {
    int eka = 3;
    int toka = 8;
    int kolmas = 4;

    keskiarvo(eka, toka, kolmas);

    // yritetään käyttää pelkkää metodin nimeä, EI TOIMI!
    System.out.print("Lukujen keskiarvo: " + keskiarvo);
}

Eli tässä yritettiin käyttää pelkkää metodin nimeä muuttujamaisesti. Toimiva tapa metodin tuloksen sijoittamisen apumuuttujaan lisäksi on suorittaa metodikutsu suoraan tulostuslauseen sisällä:

public static void main(String[] args) {
    int eka = 3;
    int toka = 8;
    int kolmas = 4;

    // kutsutaan metodia tulostuslauseessa, TOIMII!
    System.out.print("Lukujen keskiarvo: " + keskiarvo(eka, toka, kolmas));
}

Tässä siis ensin tapahtuu metodikutsu joka palauttaa arvon 5.0 joka sitten tulostetaan tulostuskomennon avulla.

Muuttuja ei ole olemassa ennen sen esittelyä, ja muuttuja on olemassa vain niiden aaltosulkujen sisällä kuin missä se on esitelty. Metodien yhteydessä tämä tarkoittaa sitä, että metodeilla on pääsy vain niihin muuttujiin, jotka ovat määritelty metodien sisällä, tai jotka metodi saa parametrina. Alla oleva esimerkki konkretisoi tilanteen, missä kasvataKolme-metodin sisällä yritetään muuttaa main-metodissa määritellyn luku-muuttujan arvoa.

// pääohjelma
public static void main(String[] args) {
    int luku = 1;
    kasvataKolmella();
}

// metodi
public static void kasvataKolmella() {
    luku = luku + 3;
}

Yllä oleva ohjelma ei toimi, sillä metodi kasvataKolmella ei näe pääohjelman muuttujaa luku. Tarkemmin ottaen, metodi kasvataKolmella ei edes tiedä mistä muuttujasta luku on kyse, sillä muuttujaa ei ole määritelty metodissa kasvataKolmella tai sen parametreissa.

Yleisemmin voi todeta, että pääohjelman muuttujat eivät näy metodien sisälle, ja metodin muuttujat eivät näy muille metodeille tai pääohjelmalle. Ainoa keino viedä metodille tietoa metodin ulkopuolelta on parametrin avulla.

Metodin parametrit

Parametrit ovat metodille annettavia arvoja, joita käytetään metodin suorituksessa. Metodin parametrit määritellään metodin ylimmällä rivillä metodin nimen jälkeen olevien sulkujen sisällä. Kun metodia kutsutaan, sen parametreille annetaan arvot kutsuvaiheessa.

Seuraavassa esimerkissä määritellään parametrillinen metodi tervehdi, jolla on int-tyyppinen parametri montakoKertaa.

public static void tervehdi(int montakoKertaa) {
    int i = 0;
    while (i < montakoKertaa) {
        System.out.println("Tervehdys!");
        i++;
    }
}

Kutsutaan metodia tervehdi siten, että parametrin montakoKertaa arvoksi asetetaan ensimmäisellä kutsulla 1 ja toisella kutsulla 3.

public static void main(String[] args) {
    tervehdi(1);
    tervehdi(3);
}
Tervehdys!
Tervehdys!
Tervehdys!
Tervehdys!

Aivan kuten Javan valmista System.out.println()-metodia kutsuttaessa, voi oman metodin kutsussa parametrina antaa lausekkeen.

public static void main(String[] args) {
    tervehdi(1 + 2);
}
Tervehdys!
Tervehdys!
Tervehdys!

Jos metodia kutsuttaessa parametriksi määritellään lauseke, evaluoidaan lauseke ennen metodikutsua. Yllä metodikutsun parametri evaluoituu arvoksi 3 ja lopullinen metodikutsu on muotoa tervehdi(3);.

Luo tehtäväpohjaan metodi public static void tulostaLukuunAsti(int luku), joka tulostaa luvut yhdestä parametrina annettuun lukuun asti. Alla on kaksi esimerkkiä metodin käytöstä.

public static void main(String[] args) {
    tulostaLukuunAsti(5);
}
1
2
3
4
5
public static void main(String[] args) {
    tulostaLukuunAsti(2);
}
1
2

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Luo tehtäväpohjaan metodi public static void tulostaLuvustaYhteen(int luku), joka tulostaa luvut parametrina annetusta luvusta yhteen asti. Alla on kaksi esimerkkiä metodin käytöstä.

public static void main(String[] args) {
    tulostaLuvustaYhteen(5);
}
5
4
3
2
1
public static void main(String[] args) {
    tulostaLuvustaYhteen(2);
}
2
1

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Metodille voidaan määritellä useita parametreja. Tällöin metodin kutsussa parametrit annetaan samassa järjestyksessä.

public static void summa(int eka, int toka) {
    System.out.println("Lukujen " + eka + " ja " + toka + " summa on " + (eka + toka));
}
summa(3, 5);

int luku1 = 2;
int luku2 = 4;

summa(luku1, luku2);
Lukujen 3 ja 5 summa on 8
Lukujen 2 ja 4 summa on 6

Kirjoita metodi public static void jakolasku(int osoittaja, int nimittaja), joka tulostaa osoittajan ja nimittäjän jakolaskun tuloksen (osoittaja / nimittaja). Muistuta mieleen mitä omituisuuksia jakolaskuihin liittyi.


Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Kirjoita metodi public static void kolmellaJaollisetValilta(int alku, int loppu), joka tulostaa kaikki kolmella jaolliset luvut annetulta väliltä. Luvut tulee tulostaa järjestyksessä pienimmästä suurimpaan.

public static void main(String[] args) {
    kolmellaJaollisetValilta(3, 6);
}
3
6
public static void main(String[] args) {
    kolmellaJaollisetValilta(2, 10);
}
3
6
9

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Metodikutsun yhteydessä parametrien arvot kopioituvat. Tämä tarkoittaa käytännössä sitä, että sekä main-metodissa että kutsuttavassa metodissa voi olla saman nimiset muuttujat, mutta muuttujien arvon muuttaminen kutsuttavan metodin sisällä ei muuta main-metodissa olevan muuttujan arvoa. Tarkastellaan tätä seuraavan ohjelman avulla.

public class Esimerkki {
    public static void main(String[] args) {
        int mista = 5;
        int mihin = 10;

        tulostaLuvut(mista, mihin);

        mista = 8;

        tulostaLuvut(mista, mihin);
    }

    public static void tulostaLuvut(int mista, int mihin) {
        while (mista < mihin) {
            System.out.println(mista);
            mista++;
        }
    }
}

Ohjelman tulostus on seuraava:

5
6
7
8
9
8
9

Alla sama askeleittaisena visualisaationa. Huomaat että main-metodissa olevat arvot jäävät kutsupinoon odottamaan metodin tulostaLuvut suorittamista. Metodissa tulostaLuvut olevien muuttujien arvojen muuttaminen ei muuta metodin main muuttujien arvoja, vaikka ne ovatkin saman nimisiä.

Metodien parametrit ovat siis erillisiä muiden metodien muuttujista (tai parametreista), vaikka niillä olisikin sama nimi. Kun metodikutsun yhteydessä metodille annetaan muuttuja, muuttujan arvo kopioituu kutsuttavan metodin metodimäärittelyssä olevan parametrimuuttujan arvoksi. Kahdessa eri metodissa olevat muuttujat ovat erillisiä toisistaan.

Tarkastellaan vielä seuraavaa esimerkkiä, missä pääohjelmassa määritellään muuttuja luku. Muuttuja luku annetaan parametrina metodille kasvataKolmella.

// pääohjelma
public static void main(String[] args) {
    int luku = 1;
    System.out.println("Pääohjelman muuttujan luku arvo: " + luku);
    kasvataKolmella(luku);
    System.out.println("Pääohjelman muuttujan luku arvo: " + luku);
}

// metodi
public static void kasvataKolmella(int luku) {
    System.out.println("Metodin parametrin luku arvo: " + luku);
    luku = luku + 3;
    System.out.println("Metodin parametrin luku arvo: " + luku);
}

Ohjelman suoritus aiheuttaa seuraavanlaisen tulostuksen.

Pääohjelman muuttujan luku arvo: 1
Metodin parametrin luku arvo: 1
Metodin parametrin luku arvo: 4
Pääohjelman muuttujan luku arvo: 1

Kun metodin sisällä kasvatetaan muuttujan luku arvoa kolmella, se onnistuu. Tämä ei kuitenkaan näy pääohjelmassa olevassa muuttujassa luku. Pääohjelmassa oleva muuttuja luku on eri kuin metodissa oleva muuttuja luku.

Parametri luku kopioidaan metodin käyttöön, eli metodia kasvataKolmella varten luodaan oma muuttuja nimeltä luku, johon pääohjelmassa olevan muuttujan luku arvo kopioidaan metodikutsun yhteydessä. Metodissa kasvataKolmella oleva muuttuja luku on olemassa vain metodin suorituksen ajan, eikä sillä ole yhteyttä pääohjelman samannimiseen muuttujaan.

Metodi voi palauttaa arvon

Metodin määrittelyssä kerrotaan palauttaako metodi arvon. Jos metodi palauttaa arvon, tulee metodimäärittelyn yhteydessä kertoa palautettavan arvon tyyppi. Muulloin määrittelyssä käytetään avainsanaa void. Tähän mennessä tekemämme metodit ovat määritelty avainsanaa void käyttäen eli eivät ole palauttaneet arvoa.

public static void kasvataKolmella() {
    ...
}

Avainsana void tarkoittaa että metodi ei palauta mitään. Jos haluamme, että metodi palauttaa arvon, tulee avainsanan void paikalle asettaa palautettavan muuttujan tyyppi. Seuraavassa esimerkissä on määritelty metodi palautetaanAinaKymppi, joka palauttaa kokonaislukutyyppisen (int) muuttujan (tässä arvon 10).

Konkreettinen arvon palautus tapahtuu komennolla return, jota seuraa palautettava arvo (tai muuttujan nimi, jonka arvo palautetaan).

public static int palautetaanAinaKymppi() {
    return 10;
}

Yllä määritelty metodi palauttaa sitä kutsuttaessa int-tyyppisen arvon 10. Jotta metodin palauttamaa arvoa voisi käyttää, tulee se ottaa talteen muuttujaan. Tämä tapahtuu samalla tavalla kuin normaali muuttujan arvon asetus, eli yhtäsuuruusmerkillä.

public static void main(String[] args) {
    int luku = palautetaanAinaKymppi();

    System.out.println("metodi palautti luvun " + luku);
}

Metodin paluuarvo sijoitetaan int-tyyppiseen muuttujaan aivan kuin mikä tahansa muukin int-arvo. Paluuarvo voi toimia myös osana mitä tahansa lauseketta.

double luku = 4 * palautetaanAinaKymppi() + (palautetaanAinaKymppi() / 2) - 8;

System.out.println("laskutoimituksen tulos " + luku);

Kaikki tähän mennessä näkemämme muuttujatyypit voidaan palauttaa metodista.

Palautettavan arvon tyyppi Esimerkki
Metodi ei palauta arvoa
public static void metodiJokaEiPalautaMitaan() {
    // metodin runko
}
Metodi palauttaa int-tyyppisen muuttujan
public static int metodiJokaPalauttaaKokonaisLuvun() {
    // metodin runko, tarvitsee return-komennon
}
Metodi palauttaa double-tyyppisen muuttujan
public static double metodiJokaPalauttaaLiukuluvun() {
    // metodin runko, tarvitsee return-komennon
}

Jos metodille määritellään paluuarvon tyyppi, on sen pakko palauttaa arvo. Esimerkiksi seuraava metodi on virheellinen.

public static int virheellinenMetodi() {
    System.out.println("Väitän palauttavani kokonaisluvun, mutten palauta sitä.");
}

Ylläolevassa metodissa tulee olla komento return, jota seuraa palautettavan arvon tyyppi.

Kirjoita metodi public static int numeroUno(), joka palauttaa arvon 1.


Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Kirjoita metodi public static String merkkijono(). Metodin tulee palauttaa itse päättämäsi merkkijono.


Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.
Komento return lopettaa metodin suorituksen

Kun metodin suorituksessa päädytään komentoon return, metodin suoritus päättyy ja metodista palautetaan arvo sitä kutsuneelle metodille.

Ohjelmointikielen kääntäjä sekä käyttämämme ohjelmointiympäristö tietää, että return-komentoa seuraavia lähdekoodirivejä ei koskaan suoriteta. Jos ohjelmoija lisää lähdekoodia return-komennon jälkeen paikkaan, mitä ei metodin suorituksessa voida koskaan saavuttaa, ohjelmointiympäristö antaa virheviesti.

Seuraavanlainen metodi on virheellinen ohjelmointiympäristön näkökulmasta.

public static int virheellinenMetodi() {
    return 10;
    System.out.println("Väitän palauttavani kokonaisluvun, mutten palauta sitä.");
}

Seuraava metodi taas toimii, sillä -- vaikka return-komennon alla on lähdekoodia -- jokainen metodin lause on mahdollista saavuttaa.

public static int virheellinenMetodi(int parametri) {
    if (parametri > 10) {
        return 10;
    }

    System.out.println("Parametrina saatu luku on kymmenen tai pienempi.");

    return parametri;
}

Mikäli metodi on muotoa public static void metodinNimi(), voi metodista palata -- tai toisin sanoen metodin suorituksen voi lopettaa kesken -- komennolla return, jota ei seuraa arvo. Esimerkiksi:

public static void jakolasku(int osoittaja, int nimittaja) {
    if (nimittaja == 0) {
        System.out.println("Nollalla ei saa jakaa!");
        return;
    }

    double tulos = 1.0 * osoittaja / nimittaja;
    System.out.println("" + osoittaja + " / " + nimittaja + " = " + tulos);
}

Palautettavan arvon ei tarvitse olla täysin ennalta määritelty, vaan se voidaan laskea. Metodista arvon palauttavalle return-komennolle voidaan antaa myös lauseke, joka evaluoituu ennen kuin arvo palautetaan.

Seuraavassa esimerkissä määritellään metodi summa, joka laskee kahden muuttujan arvon yhteen ja palauttaa summan. Yhteen laskettavien muuttujien eka ja toka arvot saadaan metodin parametrina.

public static int summa(int eka, int toka) {
    return eka + toka;
}

Kun metodin suorituksessa päädytään lauseeseen return eka + toka;, evaluoidaan lauseke eka + toka, jonka arvo lopulta palautetaan.

Metodin kutsutaan seuraavasti. Alla metodia käytetään laskemaan luvut 2 ja 7 yhteen. Metodikutsusta saatava paluuarvo asetetaan muuttujaan lukujenSumma:

int lukujenSumma = summa(2, 7);
// lukujenSumma on nyt 9

Laajennetaan edellistä esimerkkiä siten, että käyttäjä syöttää luvut.

public static void main(String[] args) {
    Scanner lukija = new Scanner(System.in);

    System.out.print("Anna ensimmäinen luku: ");
    int eka = Integer.valueOf(lukija.nextLine());

    System.out.print("Anna toinen luku: ");
    int toka = Integer.valueOf(lukija.nextLine());

    System.out.print("Luvut ovat yhteensä: " + summa(eka, toka));
}

public static int summa(int eka, int toka) {
    return eka + toka;
}

Yllä olevassa esimerkissä metodin paluuarvoa ei aseteta muuttujaan, vaan sitä käytetään suoraan osana tulostusta. Tällöin tulostuskomennon suoritus tapahtuu siten, että tietokone selvittää ensin tulostettavan merkkijonon "Luvut ovat yhteensä: " + summa(eka, toka). Ensin tietokone etsii muuttujat eka ja toka, ja kopioi niiden arvot metodin summa parametrien arvoiksi. Tämän jälkeen metodissa lasketaan parametrien arvot yhteen, jonka jälkeen summa palauttaa arvon. Tämä arvo asetetaan metodikutsun summa paikalle, jonka jälkeen summa liitetään merkkijonon "Luvut ovat yhteensä: " jatkeeksi.

Koska metodille annettavat arvot kopioidaan metodin parametreihin, metodin parametrien nimillä ja metodin kutsujan puolella määritellyillä muuttujien nimillä ei ole oikeastaan mitään tekemistä keskenään. Edellisessä esimerkissä sekä pääohjelman muuttujat että metodin parametrit olivat "sattumalta" nimetty samoin (eli eka ja toka). Seuraava toimii täysin samalla tavalla vaikka muuttujat ovatkin eri nimisiä:

public static void main(String[] args) {
    Scanner lukija = new Scanner(System.in);

    System.out.print("Anna ensimmäinen luku: ");
    int luku1 = Integer.valueOf(lukija.nextLine());

    System.out.print("Anna toinen luku: ");
    int luku2 = Integer.valueOf(lukija.nextLine());

    System.out.print("Luvut ovat yhteensä: " + summa(luku1, luku2));
}

public static int summa(int eka, int toka) {
    return eka + toka;
}

Nyt pääohjelman muuttujan luku1 arvo kopioituu metodin parametrin eka arvoksi ja pääohjelman muuttujan luku2 arvo kopioituu metodin parametrin toka arvoksi.

Täydennä tehtäväpohjassa olevaa metodia summa siten, että se laskee ja palauttaa parametrina olevien lukujen summan.

Tee metodi seuraavaan runkoon:

public static int summa(int luku1, int luku2, int luku3, int luku4) {
    // kirjoita koodia tähän
    // muista että metodissa on oltava (lopussa) return!
}

public static void main(String[] args) {
    int vastaus = summa(4, 3, 6, 1);
    System.out.println("Summa: " + vastaus);
}

Ohjelman tulostus:

Summa: 14

Huom: kun tehtävässä sanotaan että metodin pitää palauttaa jotain, tarkoittaa tämä sitä että metodissa tulee olla määritelty paluutyyppi ja return-komento jolla haluttu asia palautetaan. Metodi ei itse tulosta (eli käytä komentoa System.out.println(..)), tulostuksen hoitaa metodin kutsuja, eli tässä tapauksessa pääohjelma.


Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Tee metodi pienin, joka palauttaa parametrina saamistaan luvuista pienemmän arvon. Jos lukujen arvo on sama, voidaan palauttaa kumpi tahansa luvuista.

public static int pienin(int luku1, int luku2) {
    // kirjoita koodia tähän
    // älä tulosta metodin sisällä mitään

    // lopussa oltava komento return
}

public static void main(String[] args) {
    int vastaus =  pienin(2, 7);
    System.out.println("Pienin: " + vastaus);
}

Ohjelman tulostus:

Pienin: 2

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Tee metodi suurin, joka saa kolme lukua ja palauttaa niistä suurimman. Jos suurimpia arvoja on useita, riittää niistä jonkun palauttaminen. Tulostus tapahtuu pääohjelmassa.

public static int suurin(int luku1, int luku2, int luku3) {
    // kirjoita koodia tähän
}

public static void main(String[] args) {
    int vastaus =  suurin(2, 7, 3);
    System.out.println("Suurin: " + vastaus);
}

Ohjelman tulostus:

Suurin: 7

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Tee metodi keskiarvo, joka laskee parametrina olevien lukujen keskiarvon. Metodin sisällä tulee käyttää apuna edellä tehtyä metodia summa!

Tee metodi seuraavaan runkoon:

public static int summa(int luku1, int luku2, int luku3, int luku4) {
    // voit kopioida metodin summa toteutuksen tänne
}

public static double keskiarvo(int luku1, int luku2, int luku3, int luku4) {
    // kirjoita koodia tähän
    // laske alkioiden summa kutsumalla metodia summa
}

public static void main(String[] args) {
    double vastaus = keskiarvo(4, 3, 6, 1);
    System.out.println("Keskiarvo: " + vastaus);
}

Ohjelman tulostus:

Keskiarvo: 3.5

Muistathan miten kokonaisluku (int) muutetaan desimaaliluvuksi (double)!


Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Metodikutsujen suoritus ja kutsupino

Mistä tietokone tietää minne metodin suorituksen jälkeen tulee palata?

Java-lähdekoodin suoritusympäristö pitää kirjaa suoritettavasta metodista kutsupinossa. Kutsupino sisältää kehyksiä, joista jokainen sisältää tiedon kyseisen metodin sisäisistä muuttujista sekä niiden arvoista. Kun metodia kutsutaan, kutsupinoon luodaan uusi kehys, joka sisältää metodin sisältämät muuttujat. Kun metodin suoritus loppuu, metodiin liittyvä kehys poistetaan kutsupinosta, jolloin suoritusta jatketaan kutsupinon edeltävästä metodista.

Alla olevan visualisaation oikealla laidalla näytetään kutsupinon toimintaa. Metodikutsun yhteydessä kutsupinoon luodaan uusi kehys, joka poistetaan metodikutsusta poistuttaessa.

Kun metodia kutsutaan, kutsuvan metodin suoritus jää odottamaan kutsutun metodin suorittamista. Tätä voidaan visualisoida kutsupinon avulla. Kutsupino tarkoittaa metodien kutsumisen muodostamaa pinoa -- juuri suoritettevana oleva metodi on aina pinon päällimmäisenä, ja metodin suorituksen päättyessä palataan pinossa seuraavana olevaan metodiin. Tarkastellaan seuraavaa ohjelmaa:

public static void main(String[] args) {
    System.out.println("Hei maailma!");
    tulostaLuku();
    System.out.println("Hei hei maailma!");
}

public static void tulostaLuku() {
    System.out.println("Luku");
}

Kun ohjelma käynnistetään, suoritus alkaa main-metodin ensimmäiseltä riviltä. Tällä rivillä olevalla komennolla tulostetaan teksti "Heippa maailma!". Ohjelman kutsupino näyttää seuraavalta:

main

Kun tulostuskomento on suoritettu, siirrytään seuraavaan komentoon, missä kutsutaan metodiatulostaLuku. Metodin tulostaLuku kutsuminen siirtää ohjelman suorituksen metodin tulostaLuku alkuun, jolloin main-metodi jää odottamaan metodin tulostaLuku suorituksen loppumista. Metodin tulostaLuku sisällä ollessa kutsupino näyttää seuraavalta.

tulostaLuku
main

Kun metodi tulostaLuku on suoritettu, palataan kutsupinossa metodia tulostaLuku yhtä alempana olevaan metodiin, eli metodiin main. Kutsupinosta poistetaan tulostaLuku, ja jatketaan main-metodin suoritusta tulostaLuku-metodikutsun jälkeiseltä riviltä. Kutsupino on nyt seuraavanlainen:

main

Kutsupino ja metodin parametrit

Tarkastellaan kutsupinoa tilanteessa, missä metodille on määritelty parametreja.

public static void main(String[] args) {
    int alku = 1;
    int loppu = 5;

    tulostaTahtia(alku, loppu);
}

public static void tulostaTahtia(int alku, int loppu) {
    while (alku < loppu) {
        System.out.print("*");
        alku++;
    }
}

Ohjelman suoritus alkaa main-metodin ensimmäiseltä riviltä, jota seuraavilla riveillä luodaan muuttujat alku ja loppu, sekä asetetaan niihin arvot. Ohjelman tilanne ennen metodin tulostaTahtia-kutsumista:

main
  alku = 1
  loppu = 5

Kun metodia tulostaTahtia kutsutaan, main-metodi jää odottamaan. Metodikutsun yhteydessä metodille tulostaTahtia luodaan uudet muuttujat alku ja loppu, joihin asetetaan parametreina annetut arvot. Nämä kopioidaan main-metodin muuttujista alku ja loppu. Metodin tulostaTahtia suorituksen ensimmäisellä rivillä ohjelman tilanne on seuraavanlainen.

tulostaTahtia
  alku = 1
  loppu = 5
main
  alku = 1
  loppu = 5

Kun toistolauseessa suoritetaan komento alku++, muuttuu tällä hetkellä suoritettavaan metodiin liittyvän alku-muuttujan arvo.

tulostaTahtia
  alku = 2
  loppu = 5
main
  alku = 1
  loppu = 5

Metodin main muuttujien arvot eivät siis muutu. Metodin tulostaTahtia suoritus jatkuisi tämän jälkeen jokusen hetken. Kun metodin tulostaTahtia suoritus loppuu, palataan takaisin main-metodin suoritukseen.

main
  alku = 1
  loppu = 5

Tarkastellaan samaa ohjelman suorituksen askeleittaisella visualisoinnilla. Visualisointiin käyttämämme sovellus kasvattaa kutsupinoa alaspäin -- oikealla laidalla ylin on aina main-metodi, jonka alle tulee kutsuttavat metodit.

Kutsupino ja arvon palauttaminen metodista

Tarkastellaan vielä esimerkkiä, missä metodi palauttaa arvon. Ohjelman main-metodi kutsuu erillistä kaynnista-metodia, jossa luodaan kaksi muuttujaa, kutsutaan summa-metodia, ja tulostetaan summa-metodin palauttama arvo.

public static void main(String[] args) {
    kaynnista();
}

public static void kaynnista() {
    int eka = 5;
    int toka = 6;

    int summa = summa(eka, toka);

    System.out.println("Summa: " + summa);
}

public static int summa(int luku1, int luku2) {
    return luku1 + luku2;
}

Metodin kaynnista suorituksen alussa kutsupino näyttää seuraavalta, sillä siihen päädyttiin main-metodista. Metodilla main ei tässä esimerkissä ole omia muuttujia:

kaynnista
main

Kun kaynnista-metodissa on luotu muuttujat eka ja toka, eli sen ensimmäiset kaksi riviä on suoritettu, on tilanne seuraava:

kaynnista
  eka = 5
  toka = 6
main

Komento int summa = summa(eka, toka); luo metodiin kaynnista muuttujan summa, ja kutsuu metodia summa. Metodi kaynnista jää odottamaan. Koska metodissa summa on määritelty parametrit luku1 ja luku2, luodaan ne heti metodin suorituksen alussa, ja niihin kopioidaan parametrina annettujen muuttujien arvot.

summa
  luku1 = 5
  luku2 = 6
kaynnista
  eka = 5
  toka = 6
  summa // ei arvoa
main

Metodin summa suoritus laskee muuttujien luku1 ja luku2 arvot yhteen. Komento return palauttaa lukujen summan kutsupinossa yhtä alemmalle metodille, eli metodille kaynnista. Palautettu arvo asetetaan muuttujan summa arvoksi.

kaynnista
  eka = 5
  toka = 6
  summa = 11
main

Tämän jälkeen suoritetaan tulostuskomento, jonka jälkeen palataan main-metodiin. Kun suoritus on main-metodin lopussa, ohjelman suoritus loppuu.

Metodi kutsuu toista metodia

Kuten edellä huomattiin, metodin sisältä voi kutsua myös muita metodeja. Alla vielä esimerkki tähän liittyen. Tehdään metodi kertotaulu, joka tulostaa annetun luvun kertotaulun. Kertotaulu tulostaa rivit metodin tulostaKertotaulunRivi avulla.

public static void kertotaulu(int ylaraja) {
    int luku = 1;

    while (luku <= ylaraja) {
        tulostaKertotaulunRivi(luku, ylaraja);
        luku++;
    }
}

public static void tulostaKertotaulunRivi(int luku, int kerroin) {

    int tulostettava = luku;
    while (tulostettava <= luku * kerroin) {
        System.out.print("  " + tulostettava);
        tulostettava += luku;
    }

    System.out.println("");
}

Esimerkiksi metodikutsun kertotaulu(3) tulostus on seuraava.

1  2  3
2  4  6
3  6  9

Alla metodikutsu kertotaulu(3) visualisoituna. Huomaa, miten kutsupinossa on tieto kutsuvan metodin sisäisestä tilasta.

Tähtien tulostus

Tee metodi tulostaTahtia, joka tulostaa annetun määrän tähtiä ja rivinvaihdon.

Tee metodi seuraavaan runkoon:

public static void tulostaTahtia(int maara) {
    // yhden tähden saat tulostettua komennolla
    // System.out.print("*");
    // kutsu tulostuskomentoa n kertaa
    // tulosta lopuksi rivinvaihto komennolla
    // System.out.println("");
}

public static void main(String[] args) {
    tulostaTahtia(5);
    tulostaTahtia(3);
    tulostaTahtia(9);
}

Ohjelman tulostus:

*****
***
*********

huom: moniosaisen tehtävät voi palauttaa palvelimelle (painamalla testausnapin oikealla puolella olevaa nappia) vaikka kaikki osat eivät olisikaan tehty. Palvelin valittelee tällöin tekemättömien osien testeistä, tehdyt osat palvelin kirjaa.

Neliön tulostus

Tee metodi tulostaNelio(int sivunpituus) joka tulostaa neliön käyttäen tulostaTahtia-metodia. Siis esimerkiksi kutsu tulostaNelio(4) tulostaa seuraavaa:

****
****
****
****

Huom: tehtävässä ei riitä että tulostus näyttää oikealta, tulostaNelio-metodin sisällä neliön "rivien" tulostus tulee tehdä tulostaTahtia-metodia käyttäen.

Ohjelmaa tehdessäsi kannattaa varmistaa main:iin kirjoitetun testikoodin avulla että metodit toimivat vaaditulla tavalla.

Suorakulmion tulostus

Tee metodi tulostaSuorakulmio(int leveys, int korkeus) joka tulostaa suorakulmion käyttäen tulostaTahtia-metodia. Siis esimerkiksi kutsu tulostaSuorakulmio(17,3) tulostaa seuraavaa:

*****************
*****************
*****************

Vasemmalle nojaavan kolmion tulostus

Tee metodi tulostaKolmio(int koko) joka tulostaa kolmion käyttäen tulostaTahtia-metodia. Siis esimerkiksi kutsu tulostaKolmio(4) tulostaa seuraavaa:

*
**
***
****

Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Tähtirivin ja tyhjien tulostus

Tee metodi tulostaTyhjaa(int maara) joka tulostaa maara kappaletta välilyöntejä. Metodi ei tulosta rivinvaihtoa.

Joudut myös joko kopioimaan edellisen tehtävän vastauksestasi metodin tulostaTahtia tai toteuttamaan sen uudelleen tämän tehtävän tehtäväpohjaan.

Oikealle nojaavan kolmion tulostus

Tee metodi tulostaKolmio(int koko) joka tulostaa kolmion käyttäen tulostaTyhjaa- ja tulostaTahtia-metodeja. Siis esimerkiksi kutsu tulostaKolmio(4) tulostaa seuraavaa:

   *
  **
 ***
****

Joulukuusen tulostus

Tee metodi jouluKuusi(int korkeus) joka tulostaa joulukuusen. Joulukuusi koostuu annetun korkuisesta kolmiosta ja jalasta. Jalka on kaksi tähteä korkea ja kolme tähteä leveä ja se on keskellä kolmion pohjaa. Kuusi tulee rakentaa käyttämällä tulostukseen metodeja tulostaTyhjaa ja tulostaTahtia

Esimerkiksi kutsu jouluKuusi(4) tulostaa seuraavaa:

   *
  ***
 *****
*******
  ***
  ***

Kutsu jouluKuusi(10) tulostaa:

         *
        ***
       *****
      *******
     *********
    ***********
   *************
  ***************
 *****************
*******************
        ***
        ***

Huom: Korkeuksien jotka ovat alle 3 ei tarvitse toimia!


Tehtävään on olemassa esimerkkiratkaisu Test My Code -järjestelmässä. Esimerkkiratkaisua voi käyttää oman oppimisen tukena, ja se on tarkasteltavissa jo ennen kuin olet saanut tehtävän valmiiksi. Esimerkkiratkaisuun pääset käsiksi täältä. Huomaathan, että tehtävän voi ratkaista monella tapaa, ja tässä annettu esimerkkiratkaisu on näistä vain yksi.

Uusi ystäväsi: CrowdSorcerer

Ohjelmoidessa tarkkaa tehtävänantoa ei yleensä ole, vaan pikemminkin jonkinlainen haluttu lopputulos. Seuraavaksi onkin tarkoitus opetella kehittämään tehtävänantoja itse. Tehtävien luominen auttaa käsittämään prosessia koodaamisen taustalla: onko olennaista toteuttaa tehtävä vaikkapa ehtolauseita käyttäen, vai olisiko jokin muu lähestymistapa parempi. Näiden tehtävänantojen tekeminen auttaa myös kurssin sisältöjen omaksumisessa ja kokonaisuuksien hahmottamisessa.

Koodarit toimivat usein yhdessä, joten on hyödyllistä osata lukea toisten koodia. Myös omat ohjelmointitaidot paranevat, kun oppii huomaamaan ja välttämään yleisiä ohjelmointivirheitä. Ohjelman koodi on myös osa sen dokumentaatiota, eli koodia lukemalla oppii ymmärtämään erilaisten ohjelmien toimintaa.

Nyt sinulla on myös mahdollisuus saada kurssille juuri sinua kiinnostavia tehtäviä! Alla olevan vempaimen, CrowdSorcererin, avulla voit luoda tehtävänantoja malliratkaisuineen muille kurssilaisille. Myöhemmin kurssilla, kun olemme perehtyneet ohjelmien testaamiseen, CrowdSorcerer auttaa sinua myös erilaisten testikonseptien opettelussa. Toistaiseksi keskitymme kuitenkin yksinkertaisiin testitapauksiin.

Crowdsorcererin käyttö

Keksi ohjetta vastaava tehtävä. Tehtävän aiheena voi olla vaikkapa syötteen tulostus tai toistolauseen käyttö. Kirjoita selkeä tehtävänanto, jossa kerrot lyhyesti toteutettavan ohjelman perusidean ja esimerkiksi käytettävät muuttujat. Halutessasi voit ottaa mallia kurssin tehtävänannoista. Tarkoituksena on kuvata ongelma tarpeeksi tarkasti, jotta tehtävän tekijä osaa koodata sille ratkaisun.

Kirjoita tämän jälkeen tehtävälle malliratkaisu. Lähdekoodikentässä on valmiiksi annettuna koodipohja, jossa harmaalla taustalla merkittynä ovat rivit, joita et voi muuttaa. Osa koodista, niin kutsuttu tehtäväpohja, tulee tehtävän tekijälle näkyviin. Painamalla rivinumeroiden vieressä olevia neliöitä voit merkitä osan tehtävästä malliratkaisuksi, jolloin nuo rivit eivät näy tehtävän tekijälle. Tehtäväpohjaksi tulee ne rivit, joita et merkitse siniseksi. Painamalla "Nollaa lähdekoodi" -nappia saat palautettua malliratkaisun sen alkuperäiseen tilaan. Lähetä-nappia painamalla näet vielä luomasi tehtäväpohjan ja malliratkaisun toisistaan eroteltuina, jolloin voit tarkistaa, näyttävätkö ne siltä miltä halusit.

Toteuttamasi malliratkaisun toimivuus tarkistetaan testien avulla. Sinä määrität, miten malliratkaisusi pitäisi toimia tietyssä tilanteessa, testit toteuttavat tämän tilanteen, ja kertovat, toimiko ohjelma kuten piti. Hyvin usein ohjelmat toimivat niin, että käyttäjältä pyydetään jotain tietoa, jota ohjelma prosessoi, minkä jälkeen ohjelma palauttaa käyttäjälle uutta tietoa. Tällöin syötteellä tarkoitetaan käyttäjältä pyydettyä tietoa, ja tuloksella ohjelman palauttamaa tietoa. Tehdessäsi tehtävää, kirjoita malliratkaisun testeille syöte ja odotettu tulos niille varattuihin kenttiin. Kun lähetät tehtävän, malliratkaisua testataan antamallasi syötteellä, ja ohjelman antamaa tulosta verrataan määrittämääsi tulokseen. Jos ohjelman tuloste ei sisällä määrittämääsi tulosta, kertoo CrowdSorcerer, miten tulokset erosivat toisistaan. Syöte-tuloste-pareja on oltava vähintään yksi, mutta kannattaa keksiä useampia.

Anna lopuksi tehtävälle kuvaavat tagit, jotta tehtäviä on helpompi luokitella esimerkiksi aiheen mukaan. Vempain ehdottaa usein käytettyjä tageja, mutta voit myös keksiä omasi.

Kun lähetät tehtävän, se välitetään palvelimelle, joka tarkastaa tehtävän ja malliratkaisun toimivuuden. Tämä prosessi vie hieman aikaa, joten odotathan kärsivällisesti! Mikäli tehtävässäsi on ongelmia, saat palautteena virheviestin korjaamisen avuksi. CrowdSorcerer alleviivaa lähdekoodin virheelliset kohdat punaisella. Kun muokkaat virheellistä riviä, punainen alleviivaus katoaa kyseiseltä riviltä. Voit lähettää tehtävän uudelleen niin monta kertaa kuin haluat, ja viimeisin hyväksytysti läpimennyt tehtävä annetaan muiden opiskelijoiden arvioitavaksi seuraavalla viikolla.

Alla on vielä video, joka ohjeistaa CrowdSorcererin käyttöön. Huomaathan, että siinä on hieman vanhempi versio kilkkeestä. Perustoiminnot eivät ole muuttuneet juurikaan, joten videon sisältö on edelleen hyödyksi.

Suunnittele oma tehtävä: Ehtolause

Tee tehtävä, jonka tarkoitus on laittaa opiskelija koodaamaan ohjelma, joka lukee käyttäjältä kokonaislukusyötteen, tarkastelee sitä ehtolauseen avulla ja tulostaa merkkijonon. Anna testejä vasten syöte-esimerkki ja ohjelman tuloste tuolla syötteellä.

Tehtävien luomistehtävät vastaavat kurssin pisteytyksessä ohjelmointitehtävää.

Yhteenveto

Toisessa osassa kerrattiin tulostamista, muuttujia, laskemista, syötteen käsittelyä, vertailua sekä toistamista. Samalla tutustuttiin usein toistuviin osaongelmiin sekä niiden ratkaisumalleihin. Materiaalissa käsiteltiin myös loogisia operaatioita, joiden avulla ehto- ja toistolauseiden ehdoista saa monipuolisempia, sekä metodeja, joiden avulla ohjelman toiminnallisuutta saa jaettua useampaan osaan.

Vastaa vielä alla olevaan kyselyyn.

Sisällysluettelo