Osa 1

Ehtolauseet ja vaihtoehtoinen toiminta

Ohjelmamme ovat tähän mennessä olleet lineaarisia eli ohjelmien suoritus on tapahtunut ylhäältä alaspäin ilman suuria yllätyksiä tai vaihtoehtoja. Ohjelmiin halutaan kuitenkin usein vaihtoehtoista toiminnallisuutta, eli toiminnallisuutta joka riippuu tavalla tai toisella ohjelmassa olevien muuttujien tilasta.

Jotta ohjelman suoritus voisi haarautua esimerkiksi käyttäjän antaman syötteen perusteella, tarvitsemme käyttöömme ehtolauseen. Yksinkertaisin ehtolause on seuraavanlainen.

System.out.println("Hei maailma!");
if (true) {
    System.out.println("Et voi välttää tätä koodia!");
}
Esimerkkitulostus

Hei maailma! Et voi välttää tätä koodia!

Ehtolause alkaa avainsanalla if, jota seuraa sulut. Sulkujen sisälle asetetaan lauseke, joka evaluoidaan kun ehtolause saavutetaan. Evaluoinnin tulos on totuusarvo, yllä evaluointia ei tehty, vaan ehtolauseessa käytettiin suoraan totuusarvoa.

Sulkuja seuraa lohko, joka määritellään avaavan aaltosulun { ja sulkevan aaltosulun } sisään. Lohkon sisällä oleva lähdekoodi mikäli sulkujen sisälle asetettu lauseke evaluoidaan todeksi (true).

Tarkastellaan esimerkkiä, missä ehtolauseen lausekkeessa vertaillaan lukuja.

int luku = 11;
if (luku > 10) {
    System.out.println("Luku oli suurempi kuin 10");
}

Jos ehtolauseen lauseke evaluoidaan todeksi, yllä "jos muuttujassa luku oleva arvo on suurempi kuin 10", ohjelman suoritus siirtyy ehtolauseen määrittelemään lohkoon. Jos taas lauseke on epätotta, ohjelman suoritus siirtyy ehtolauseeseen liittyvän lohkon päättävän aaltosulun jälkeiseen lauseeseen.

Huomaa, että if -lauseen perään ei tule puolipistettä, sillä lause ei lopu ehto-osan jälkeen.

Loading

Ohjelmakoodin sisennyksestä ja lohkoista

Lohkolla tarkoitetaan aaltosulkujen rajaamaa aluetta. Ohjelman sisältävä lähdekooditiedosto sisältää merkkijonon public class, jota seuraa ohjelman nimi ja lohkon avaava aaltosulku. Lohko päättyy sulkevaan aaltosulkuun. Alla olevassa kuvassa on näytettynä värjättynä ohjelman lohko.

Ohjelmissa toistuva rimpsu public static void main(String[] args) aloittaa oman lohkon, jonka sisällä oleva lähdekoodi suoritetaan kun ohjelma käynnistetään — rimpsu on oikeastaan jokaisen ohjelman aloituskohta. Yllä olevassa esimerkissä on todellisuudessa kaksi lohkoa, kuten alla olevasta kuvasta huomaamme.

Lohkot määrittelevät ohjelman rakennetta ja rajaavat ohjelmaa. Aaltosuluille tulee aina löytyä pari: koodi, josta josta puuttuu lohkon päättävä (tai aloittava) aaltosulku, on virheellinen.

Myös ehtolause aloittaa lohkon.

Lohkoihin liittyy ohjelman rakenteen ja toiminnan määrittelyn lisäksi luettavuuteen liittyvä seikka. Lohkojen sisällä oleva koodi sisennetään. Esimerkiksi ehtolauseeseen liittyvän lohkon sisältämä lähdekoodi sisennetään neljä välilyöntiä sisemmälle kuin ehtolauseen aloittava if-komento. Neljä merkkiä saa myös tabulaattorimerkillä (q:n vasemmalla puolella oleva näppäin). Kun lohko sulkeutuu, eli tulee }-merkki, sisennys loppuu. }-merkki on samalla tasolla kuin ehtolauseen aloittanut if-komento.

Alla oleva esimerkki on sisennetty väärin.

if (luku > 10) {
luku = 9;
}

Alla oleva esimerkki on sisennetty oikein.

if (luku > 10) {
    luku = 9;
}
Loading

Vertailuoperaattorit

Vertailuoperaattoreita ovat seuraavat:

  • >suurempi kuin
  • >=suurempi tai yhtä suuri kuin
  • <pienempi kuin
  • <= pienempi tai yhtä suuri kuin
  • == yhtä suuri kuin
  • != erisuuri kuin
int luku = 55;

if (luku != 0) {
    System.out.println("Luku oli erisuuri kuin 0");
}

if (luku >= 1000) {
    System.out.println("Luku oli vähintään 1000");
}
Esimerkkitulostus

Luku oli erisuuri kuin 0

Loading
Loading

Muulloin eli else

Jos ehtolauseen sulkujen sisällä oleva lauseke evaluoituu epätodeksi, ohjelmakoodin suoritus siirtyy ehtolauseen lohkon lopettavan aaltosulun seuraavaan lauseeseen. Tämä ei aina ole toivottua, vaan usein halutaan luoda vaihtoehtoinen toiminta tilanteeseen, missä ehtolauseen lauseke on epätotta.

Tämä onnistuu if-komennon yhteydessä käytettävän else-komennon avulla.

int luku = 4;

if (luku > 5) {
    System.out.println("Lukusi on suurempi kuin viisi!");
} else {
    System.out.println("Lukusi on viisi tai alle!");
}
Esimerkkitulostus

Lukusi on viisi tai alle!

Jos ehtolauseeseen on määritelty else-haara, suoritetaan else-haaran määrittelemä lohko jos ehtolauseen ehto ei ole totta. Komento else tulee samalle riville if-komennon määrittelemän lohkon lopettavan aaltosulun kanssa.

Loading
Loading

Lisää vaihtoehtoja: else if

Jos vaihtoehtoja on useampia käytetään else if-komentoa. Komento else if on kuin else, mutta lisäehdolla. else if tulee if-ehdon jälkeen, ja niitä voi olla useita.

int luku = 3;

if (luku == 1) {
    System.out.println("Luku on yksi");
} else if (luku == 2) {
    System.out.println("Lukuna on kaksi");
} else if (luku == 3) {
    System.out.println("Kolme lienee lukuna!");
} else {
    System.out.println("Jotain muuta!");
}
Esimerkkitulostus

Kolme lienee lukuna!

Luetaan yllä oleva esimerkki: 'Jos luku on yksi, tulosta "Luku on yksi", muuten jos luku on kaksi, tulosta "Lukuna on kaksi", muuten jos lukuna on kolme, tulosta "Kolme lienee lukuna!". Muulloin, tulosta "Jotain muuta!"'.

Yllä olevan ohjelman askeleittainen visualisointi:

Loading...
Loading

Vertailujen suoritusjärjestys

Vertailut suoritetaan järjestyksessä ylhäältä alaspäin. Kun suorituksessa päästään ehtolauseeseen, jonka ehto on totta, suoritetaan lohko ja lopetetaan vertailu.

int luku = 5;

if (luku == 0) {
    System.out.println("Luku on nolla.");
} else if (luku > 0) {
    System.out.println("Luku on suurempi kuin nolla.");
} else if (luku > 2) {
    System.out.println("Luku on suurempi kuin kaksi.");
} else {
    System.out.println("Luku on pienempi kuin nolla.");
}
Esimerkkitulostus

Luku on suurempi kuin nolla.

Yllä oleva esimerkki tulostaa merkkijonon "Luku on suurempi kuin nolla." vaikka myös ehto luku > 2 on totta. Vertailu lopetetaan ensimmäiseen valintakäskyyn, jonka ehto on totta.

Loading

Ehtolauseen lauseke ja totuusarvomuuttuja

Ehtolauseen sulkuihin asetettavan arvon tulee olla lausekkeen evaluoinnin jälkeen totuusarvotyyppinen. Totuusarvomuuttujan tyyppi on boolean ja arvo true tai false.

boolean onkoTotta = true;
System.out.println("Totuusarvomuuttujan arvo on " + onkoTotta);
Esimerkkitulostus

Totuusarvomuuttujan arvo on true

Ehtolauseen voi suorittaa myös seuraavasti:

boolean onkoTotta = true;
if (onkoTotta) {
    System.out.println("Aika vinhaa!");
}
Esimerkkitulostus

Aika vinhaa!

Vertailuoperaattoreita voi käyttää myös ehtojen ulkopuolella. Tällöin vertailun tuloksena saatu totuusarvo asetetaan talteen totuusarvomuuttujaan myöhempää käyttöä varten.

int eka = 1;
int toka = 3;
boolean onkoSuurempi = eka > toka;

Yllä olevassa esimerkissä totuusarvomuuttuja onkoSuurempi sisältää nyt totuusarvon false. Yllä olevaa esimerkkiä voi myös jatkaa ja ottaa siihen mukaan ehtolauseen.

int eka = 1;
int toka = 3;
boolean onkoPienempi = eka < toka;

if (onkoPienempi) {
    System.out.println("1 on pienempi kuin 3!");
}

Yllä olevassa kuvassa ohjelmakoodia on suoritettu niin pitkään, että ohjelman muuttujat on luotu ja niihin on asetettu arvot. Muuttujassa onkoPienempi on arvona true. Seuraavana suoritetaan vertailu if (onkoPienempi) — muuttujaan onkoPienempi liittyvä arvo löytyy sen lokerosta, ja lopulta ohjelma tulostaa:

Esimerkkitulostus

1 on pienempi kuin 3!

Loading

Ehtolauseet ja merkkijonojen vertailu

Siinä missä kokonaislukujen, liukulukujen, ja totuusarvojen samuutta voi verrata kahdella yhtäsuuruusmerkillä (muuttuja1 == muuttuja2), ei merkkijonojen samuuden vertailu kahdella yhtäsuuruusmerkillä onnistu.

Voit kokeilla tätä seuraavalla ohjelmalla:

Scanner lukija = new Scanner(System.in);

System.out.println("Syötä ensimmäinen merkkijono");
String eka = lukija.nextLine();
System.out.println("Syötä toinen merkkijono");
String toka = lukija.nextLine();

if (eka == toka) {
    System.out.println("Merkkijonot olivat samat!");
} else {
    System.out.println("Merkkijonot olivat eri!");
}
Esimerkkitulostus

Syötä ensimmäinen merkkijono sama Syötä toinen merkkijono sama Merkkijonot olivat eri!

Esimerkkitulostus

Syötä ensimmäinen merkkijono sama Syötä toinen merkkijono eri Merkkijonot olivat eri!

Tämä liittyy merkkijonojen sisäiseen toimintaan sekä siihen, miten muuttujien vertailu on Javassa toteutettu. Käytännössä vertailun toimintaan vaikuttaa se, kuinka paljon tietoa muuttuja voi sisältää — merkkijonot voivat sisältää äärettömän määrän merkkejä, kun taas kokonaisluvut, liukuluvut ja totuusarvot sisältävät aina yhden luvun tai arvon. Muuttujia, jotka sisältävät aina vain yhden luvun tai arvon voi verrata yhtäsuuruusmerkillä, kun taas enemmän tietoa sisältävillä muuttujille tällainen vertailu ei toimi. Palaamme tähän tarkemmin myöhemmin tällä kurssilla.

Merkkijonojen vertailussa käytetään merkkijonomuuttujiin liittyvää equals-komentoa. Komento toimii seuraavalla tavalla:

Scanner lukija = new Scanner(System.in);

System.out.println("Syötä merkkijono");
String syote = lukija.nextLine();

if (syote.equals("merkkijono")) {
    System.out.println("Luit ohjeet oikein, hyvä!");
} else {
    System.out.println("Metsään meni!");
}
Esimerkkitulostus

Syötä merkkijono ok! Metsään meni!

Esimerkkitulostus

Syötä merkkijono merkkijono Luit ohjeet oikein, hyvä!

Komento equals kirjoitetaan merkkijonomuuttujan jälkeen siten, että se kiinnitetään pisteellä vertailtavaan muuttujaan. Komennolle annetaan parametrina merkkijono, johon muuttujaa vertaillaan. Mikäli merkkijonomuuttujaa vertaillaan suoraan merkkijonoon, voi merkkijonon asettaa hipsuilla merkittynä equals-komennon sulkujen sisään. Muulloin sulkujen sisään asetetaan sen merkkijonomuuttujan nimi, johon merkkijonomuuttujan sisältämää merkkijonoa verrataan.

Alla olevassa esimerkissä luetaan käyttäjältä kaksi merkkijonoa. Ensin tarkastetaan ovatko syötetyt merkkijonot samat, jonka jälkeen tarkastetaan onko syötettyjen merkkijonojen arvo "kaksi merkkijonoa".

Scanner lukija = new Scanner(System.in);

System.out.println("Syötä kaksi merkkijonoa");
String eka = lukija.nextLine();
String toka = lukija.nextLine();

if (eka.equals(toka)) {
    System.out.println("Merkkijonot olivat samat!");
} else {
    System.out.println("Merkkijonot olivat eri!");
}

if (eka.equals("kaksi merkkijonoa")) {
    System.out.println("Nokkelaa!");
}

if (toka.equals("kaksi merkkijonoa")) {
    System.out.println("Ovelaa!");
}
Esimerkkitulostus

Syötä kaksi merkkijonoa hei maailma Merkkijonot olivat eri!

Esimerkkitulostus

Syötä kaksi merkkijonoa kaksi merkkijonoa maailma Merkkijonot olivat eri! Nokkelaa!

Esimerkkitulostus

Syötä kaksi merkkijonoa samat samat Merkkijonot olivat samat!

Loading
Loading

Loogiset operaatiot

Ehtolauseen lauseke voi koostua 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 molemmat 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 >= 5 && luku <= 10) {
    System.out.println("On! :)");
} else {
    System.out.println("Ei ollut :(");
}
Esimerkkitulostus

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 :(");
}
Esimerkkitulostus

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.");
}
Esimerkkitulostus

Luku on suurempi tai yhtäsuuri kuin 4.

Alla on kuvattuna lausekkeiden toimintaa kun lausekkeissa on loogisia operaatioita.

lukuluku > 0luku < 10luku > 0 && luku < 10!(luku > 0 && luku < 10)luku > 0 || luku < 10
-1falsetruefalsetruetrue
0falsetruefalsetruetrue
1truetruetruefalsetrue
9truetruetruefalsetrue
10truefalsefalsetruetrue
Loading

Ehtolauseiden suoritusjärjestys

Tutustutaan ehtolauseiden suoritusjärjestykseen klassisen ohjelmointiongelman kautta.

'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:

  • Tee ohjelma, joka lukee luvun käyttäjältä ja tulostaa sen.
  • Jos luku on jaollinen kolmella, tulosta luvun sijaan merkkijono "Fizz".
  • Jos luku on jaollinen viidellä, tulosta luvun sijaan merkkijono "Buzz".
  • 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);
}
Esimerkkitulostus

3 Fizz

Esimerkkitulostus

4 4

Esimerkkitulostus

5 Buzz

Esimerkkitulostus

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);
}
Esimerkkitulostus

2 2

Esimerkkitulostus

5 Buzz

Esimerkkitulostus

30 FizzBuzz

Loading
Loading
Pääsit aliluvun loppuun! Jatka tästä seuraavaan osaan:

Muistathan tarkistaa pistetilanteesi materiaalin oikeassa alareunassa olevasta pallosta!