Tehtävät
Aloituskysely

Kurssiin kuuluu muutama kysely, joiden tavoitteena on parantaa kurssia. Vastaa ensimmäiseen kyselyyn osoitteessa https://elomake.helsinki.fi/lomakkeet/76573/lomake.html.

Käy vastaamassa ensin kyselyyn ja ruksaa sen jälkeen allaoleva tekstikenttä. Jos allaolevan tekstikentän ruksaaminen ei onnistu, varmista että olet kirjautunut tälle sivulle. Kirjautuminen onnistuu sivun oikeasta ylälaidasta.

Ohjelma ja lähdekoodi

Tietokoneohjelmia verrataan usein leipomiseen. Tietokone on aloitteleva leipuri, tietokone-ohjelma kokkikirjasta saatu resepti, ja tietokoneen resurssit -- esimerkiksi näyttö, tietokoneelle tallennettu tiedosto ja verkkokortti -- leipomiseen käytettäviä raaka-aineita. Tietokone noudattaa sille annettua reseptiä säntillisesti askel kerrallaan ilman ymmärrystä tai tietoa hamaan ikuisuuteen asti. Jos reseptissä on kirjoitusvirhe, ei tietokone tiedä miten sen tulee toimia. Tietokone ei myöskään osaa reagoida vaikkapa rikkinäiseen uuniin, ellei resepti kerro täsmällisesti miten rikkinäinen uuni tunnistetaan ja mitä uunin rikkoutuessa tulee tehdä.

Aloittelevalla leipurilla ja tietokoneella on muutamia merkittäviä eroja. Tietokone pystyy suorittamaan miljoonia yksittäisiä käskyjä sekunnissa, kun aloittelevalla leipurilla voi mennä yhteen reseptissä olevan käskyn noudattamiseen minuutteja. Toisaalta, aloitteleva leipuri oppii ja kehittyy leipoessaan. Hän voi luovasti kokeilla uusia raaka-aineita ja reseptejä, sekä kehitellä itselleen paremmin sopivia työskentelytapoja. Tietokoneella ei tällaista kehittymiskykyä ole, ellei sitä erikseen -- askel askeleelta -- ole sille annettuun reseptiin kirjoitettu. Tällä kurssilla opit kirjoittamaan tietokoneelle tarkoitettuja reseptejä.

Tietokoneelle annettava resepti eli ohjelma on järjestetty joukko ennalta laadittuja käskyjä, joita tietokone noudattaa yksi kerrallaan. Ohjelmoijat eivät tyypillisesti ohjelmoi tietokoneen suoraan ymmärtämiä käskyjä käyttäen, vaan ohjelmoinnissa käytetään ihmisen kirjoitettavaksi ja luettavaksi tarkoitettua ohjelmointikieltä, kuten tällä kurssilla käytettyä Javaa. Ohjelmoijan kirjoittamaa ohjelmakoodia kutsutaan lähdekoodiksi. Lähdekoodi käännetään ohjelmaksi tähän erityisesti tarkoitetun ja kirjoitetun kääntäjäohjelman avulla.

Lähdekoodi koostuu lauseista (statement) ja lausekkeista (expression), joita voidaan lukea rivi riviltä ylhäältä alaspäin ja vasemmalta oikealle. Esimerkiksi tekstin "Hei maailma" tulostuksessa käytetään Java-ohjelmointikielellä lausetta:

  System.out.println("Hei maailma");

Lause System.out.println on Java-ohjelmointikielen valmiiksi tarjoama komento, jota käytetään merkkijonon tulostamiseen. Komento tulostaa sille sulkeiden sisällä hipsuissa annetun merkkijonon. Komennon pääte ln on lyhenne sanasta line, eli komento tulostaa merkkijonon jälkeen myös rivinvaihdon.

Lauseen loppuun kirjoitetaan puolipiste ;.

Ohjelmarunko

Java-ohjelmat vaativat toimiakseen kurssin aikana tutuksi tulevan rungon. Ohjelman runko on seuraavanlainen.

public class Esimerkki {
    public static void main(String[] args) {

        // Tänne kirjoitetaan ohjelman käyttämät lauseet
        System.out.println("Tulostettava teksti");

    }
}

Kirjoittamamme ohjelman suoritus alkaa riviä public static void main(String[] args) { seuraavalta riviltä, ja ohjelman suoritus päättyy sulkevaan aaltosulkuun }. Lauseet suoritetaan yksi kerrallaan. Tällä hetkellä ainoa suoritettava lause on System.out.println("Tulostettava teksti");, mikä tulostaa tekstin "Tulostettava teksti".

Jatkossa materiaalin esimerkeissä ei aina erikseen näytetä ohjelmarunkoa, mutta voit olettaa, että se tarvitaan.

Tehtäväpohjassa on seuraavanlainen ohjelmarunko:

public class Nimi {

    public static void main(String[] args) {
        // Kirjoita ohjelmasi tähän alle

    }
}

Rivi "// Kirjoita ohjelmasi tähän alle" on kommenttirivi, jota tietokone ei ota huomioon ohjelmaa suoritettaessa. Lisää kommenttirivin alle lause, joka tulostaa merkkijonon "Ada Lovelace" ja suorita ohjelma. Ohjelman tulostuksen tulee olla seuraavanlainen:

Ada Lovelace

Kun olet tehnyt tehtävän ja huomaat, että ohjelma tulostaa halutun merkkijonon, palauta tehtävä TMC:lle. Tutustu tämän jälkeen halutessasi lisää Ada Lovelaceen, joka oli yksi ensimmäisistä ohjelmoijista.

Lähdekoodin sijainti

Lähdekoodi ladataan ja tallennetaan tietokoneen kovalevylle. Näet lähdekoodin tarkemman sijainnin klikkaamalla projektin nimeä (esim. Osa01_01.AdaLovelace) hiiren oikealla näppäimellä ja valitsemalla Properties. Tämä avaa projektiin liittyvän asetusikkunan. Ikkunan Sources-valikossa löytyy tieto projektin kansiosta (Project Folder).

Kyseiseen kansioon voi mennä myös ilman NetBeansia. Huomaat, että projektin kansiossa on useita tiedostoja. Lähdekoodit, joita muokkaamme ja luomme tällä kurssilla sijaitsevat aina kansiossa src eli sources.

Lähdekooditiedostojen nimi päättyy merkkijonoon .java, ja tiedoston nimi on sama kuin ohjelman nimi. Esimerkiksi jos ohjelma alkaa sanoilla public class Esimerkki, löytyy siihen liittyvä lähdekoodi tiedostosta Esimerkki.java.

Ohjelman osia

Kommentit

Lähdekoodia kirjoitetaan myös muita ohjelmoijia varten, joten siitä halutaan mahdollisimman ymmärrettävää. Ohjelmoija voi kommentoida kirjoittamaansa koodia selkeyttääkseen sitä tai lisätäkseen muistiinpanoja kahdella eri tavalla.

  • Yhden rivin kommentit aloitetaan kahdella vinoviivalla, //. Kaikki kahta vinoviivaa seuraava samalla rivillä oleva teksti tulkitaan kommentiksi.
  • Useamman rivin kommentit aloitetaan yhdellä vinoviivalla ja tähdellä /* ja lopetetaan tähdellä ja vinoviivalla */. Kaikki useamman rivin kommentin aloittavan ja lopettavan alueen välillä tulkitaan kommentiksi.

Alla on esimerkki ohjelmasta, jossa kumpikin kommenttityyppi on käytössä.

public class Kommentteja {
    public static void main(String[] args) {

        // Tulostetaan
        System.out.println("Tulostettava teksti");
        System.out.println("Lisää tulostettavaa!");

        /*
          Seuraavaksi:
            - lisää tulostamisesta
            - lisää harjoittelua
            - muuttujat
            - ...
        */

        // System.out.println("Muuta tulostettavaa");
    }
}

Esimerkin alin rivi esittelee erityisen kätevän käyttökohteen kommenteille. Kirjoitettua koodia ei tarvitse poistaa jos haluaa tilapäisesti kokeilla jotain.

Tee ohjelma, jonka tulostus on seuraava:

Hei Maailma!
(Ja Mualima!)

Tulostaminen

Käytimme edellä lausetta System.out.println("tulostettava"); merkkijonon tulostamiseen, mutta toinenkin vaihtoehto on olemassa. Tulostamiseen on eri kaksi lausetta:

  • System.out.println("sana"); tulostaa tekstin "sana" ja loppurivinvaihdon
  • System.out.print("sana"); tulostaa tekstin "sana" ilman loppurivinvaihtoa

Tulostettavan tekstin osana voi olla myös erikoismerkkejä, joista tärkein on rivinvaihto. Rivinvaihto ilmaistaan kenoviivalla ja n-merkillä seuraavasti: \n. Erikoismerkkejä on muitakin.

  System.out.println("Ensimmäinen\nToinen\nKolmas");

Ylläoleva lause tulostaa seuraavaa:

Ensimmäinen
Toinen
Kolmas

Komennon parametrit

Tulostuslauseen tulostama tieto eli komennon parametrit annetaan tulostuskomennolle lisäämällä ne lauseen perässä olevien sulkujen () sisään. Esimerkiksi System.out.println -komennon parametriksi voidaan antaa merkkijono hei hipsujen sisällä seuraavasti: System.out.println("hei").

Puolipiste

Puolipisteellä ; erotetaan lauseet toisistaan. Voisimme oikeastaan kirjoittaa koko ohjelman yhdelle riville -- mikä ei kuitenkaan ole kovin ymmärrettävää.

System.out.print("Hei "); System.out.print("maailma"); System.out.print("!\n");
Hei maailma!

Vaikka ylläoleva esimerkki toimii, on rivinvaihtojen käyttö tärkeää muita ohjelmoijia ajatellen. Selkeä lähdekoodin osien erottelu vaatii rivinvaihtojen käyttöä. Tätä ja muita lähdekoodin luettavuuteen liittyviä seikkoja tullaan painottamaan tällä kurssilla.

Ohjelmointityylistä

Vaikka tietokone ja käyttämämme ohjelmointikieli ei aseta rajoituksia kirjoitettavan ohjelmakoodin ulkoasulle, olemme osana ohjelmoinnin opetuksen ja oppimisen tutkimista huomanneet että ohjelmoijan -- tai opiskelevan ohjelmoijan -- kirjoittaman koodin ulkoasulla on merkitystä myös oppimisen kannalta. Esimerkiksi lähdekoodin luettavuus ja sisennyksen säännönmukaisuus ovat asioita, jotka vaikuttavat lähdekoodin ymmärrettävyyteen, ja sitä kautta myös oppimistuloksiin. Seuraava koodi on säännönmukaisesti sisennettyä.

public class Esimerkki {
    public static void main(String[] args) {
        System.out.println("Heippa vaan! Tämä koodi on siististi sisennetty.");
        System.out.println("public class -- ei sisennystä.");
        System.out.println("public static -- neljän merkin sisennys.");
        System.out.println("public static ... sisällä -- kahdeksan merkin sisennys -- tai enemmän.");
    }
}

Tämä koodi taas ei ole kovin ymmärrettävää.

               public class Esimerkki {
    public static void main(String[] args) {
System.out.println("Heippa vaan! Tämä koodi on siististi sisennetty.");
                              System.out.println("public class -- ei sisennystä.");
  System.out.println("public static -- neljän merkin sisennys.");
System.out.println("public static ... sisällä -- kahdeksan merkin sisennys -- tai enemmän.");}}

Kurssilla käytettävässä Test My Code-ympäristössä tulee mukana Checkstyle-niminen työväline, joka ohjaa hyvään ohjelmointityyliin. Checkstyle tarkistaa mm. koodin sisennystä, metodien nimeämistä, tyhjiä lohkoja ja paljon muuta -- kurssin edetessä nämä käsitteet tulevat tutuiksi. Tällä kurssilla Checkstyle tarkistaa lähes ainoastaan tyylivirheitä -- ohjeita tulee kurssin edetessä.

Tyylivirheet näytetään ohjelmointiympäristössä keltaisella, ja normaalit testi-ilmoitukset punaisella. Kurssilla tutuksi tuleva tehtävän edistymispalkki muuttuu myöskin keltaiseksi, jos koodissa havaitaan tyylivirheitä. Vaikkakin näppäinyhdistelmä alt + shift + f (OS X control + shift + f) auttaa useimpien tyylivirheiden korjaamiseen, on koodia syytä kirjoittaa oikein alusta alkaen.

Lähdekoodi tulee sisentää oikein

Javassa koodia sisennetään neljän välilyönnin tai yhden tabulaattorin verran jokaisen lohkon kohdalla. Käytä sisentämiseen joko välilyöntejä tai tabulaattoreita. Joissakin tapauksissa sisennys saattaa hajota mikäli käytät molempia. NetBeans auttaa tässä kun painat kirjainyhdistelmää "alt + shift + f" (OS X "control + shift + f").

Jatkossa ohjelmakoodi tulee sisentää oikein myös tehtävissä. Jos sisennys on väärin, ei ohjelmointiympäristö hyväksy tehtävää.

Tee ohjelma, jonka tulostus on seuraava:

    *
   ***
  *****
 *******
*********
    *
Lyhenne "sout"

Kirjoitit todennäköisesti aika monta kertaa System.out.println("..."). Kokeile kirjoittaa NetBeans:iin (main:in sisään) tyhjälle riville sout ja paina tabulaatoria (näppäin q:n vasemmalla puolella). Mitä tapahtuu? Tämä pieni apuväline säästänee jatkossa runsaasti aikaasi.

Muuttuja ja sijoitus

Keskeinen käsite ohjelmoinnissa on muuttuja, jota käytetään tiedon tallentamiseen myöhempää käyttöä varten.

Muuttujan esittely

Muuttujaan säilöttävällä tiedolla on aina tyyppi. Tyyppejä ovat esimerkiksi teksti eli merkkijono (String), kokonaisluku (int), liukuluku (double) eli desimaaliluku ja ja totuusarvo (boolean).

Muuttuja esitellään kertomalla ensin sen tyyppi, jota seuraa muuttujalle annettava nimi. Muuttujaan asetetaan arvo yhtäsuuruusmerkillä (=).

int kuukausia = 12;

Yllä olevassa lauseessa luodaan kokonaisluku-tyyppinen muuttuja (int), jolle annetaan nimeksi kuukausia. Muuttujaan kuukausia asetetaan arvo 12. Lause luetaan "muuttuja kuukausia saa arvon 12".

Muuttujan arvo voidaan yhdistää tulostuslauseelle annettavaan parametriin +-merkillä seuraavan esimerkin mukaisesti.

String teksti = "sisältää tekstiä";
int kokonaisluku = 123;
double liukuluku = 3.141592653;

System.out.println("Tekstimuuttuja: " + teksti);
System.out.println("Kokonaislukumuuttuja: " + kokonaisluku);
System.out.println("Liukulukumuuttuja: " + liukuluku);

Tulostus:

Tekstimuuttuja: sisältää tekstiä
Kokonaislukumuuttuja: 123
Liukulukumuuttuja: 3.141592653
Ohjelman suorituksen visualisointi

Java-ohjelmien suorituksen etenemistä voi tarkastella osoitteessa https://www.pythontutor.com/java.html olevan palvelun avulla. Alla on liitettynä edellinen esimerkki visualisoituna. Painamalla nappia "Forward" ohjelman suoritus etenee.

Tehtäväpohja sisältää ohjelman, joka tulostaa seuraavaa.

Kanoja:
3
Pekonia (kg):
5.5
Traktori:
Ei ole!

Tässä vielä tiivistelmä:
3
5.5
Ei ole!

Muuta ohjelmaa annetuista kohdista niin että tuloste on:

Kanoja:
9000
Pekonia (kg):
0.1
Traktori:
Zetor

Tässä vielä tiivistelmä:
9000
0.1
Zetor

Muuttujaan asetetun arvon muuttaminen

Muuttujaan asetettu arvo säilyy kunnes muuttujaan asetetaan toinen arvo. Huomaa että muuttujan tyyppi kirjoitetaan vain kun muuttuja esitellään ohjelmassa ensimmäistä kertaa.

int luku = 123;
System.out.println("Muuttujan arvo on " + luku);

luku = 42;
System.out.println("Muuttujan arvo on " + luku);

Tulostus:

Muuttujan arvo on 123
Muuttujan arvo on 42

Tarkastellaan edellisen ohjelmakoodin suoritusta askel askeleelta. Kun muuttuja esitellään ohjelmakoodissa ensimmäistä kertaa, eli sekä muuttujan tyyppi (tässä int) että sen nimi (tässä luku) kerrotaan tietokoneelle, tietokone luo muuttujaa varten "nimetyn lokeron". Tämän jälkeen yhtäsuuruusmerkin oikealla puolella oleva arvo kopioidaan tähän nimettyyn lokeroon.

Kun ohjelmakoodissa viitataan muuttujaan sen nimellä -- tässä halutaan tulostaa merkkijono "Muuttujan arvo on " sekä muuttujan luku arvo, muuttujan luku arvo haetaan sen nimellä löytyvästä lokerosta.

Kun muuttujaan asetetaan arvo (tässä luku = 42), tarkistetaan ensin löytyykö muuttujan nimistä lokeroa. Jos lokero löytyy, uusi arvo kopioidaan lokeroon vanhan arvon tilalle ja vanha arvo katoaa. Jos muuttujan nimellä ei löydy lokeroa, ohjelman suoritus päättyy virheilmoitukseen tai ohjelmaa ei voida käynnistää.

Seuraavaksi ohjelmakoodissa viitataan taas muuttujaan sen nimellä -- tässäkin halutaan tulostaa merkkijono "Muuttujan arvo on " sekä muuttujan luku arvo. Toimitaan kuten normaalisti, eli haetaan muuttujan luku arvo sen nimellä löytyvästä lokerosta.

Muuttujan tyyppi pysyy

Kun muuttujan tyyppi on kertaalleen määritelty, ei sitä voi enää muuttaa. Merkkijonoa ei siis voi esimerkiksi asettaa kokonaislukutyyppiseen muuttujaan, eikä merkkijonomuuttujaan voi asettaa kokonaislukua.

String merkkijono = "Hei maailma!";
merkkijono = 42; // Ei onnistu

int luku = 10;
merkkijono = luku; // Ei myöskään onnistu

Poikkeus kuitenkin löytyy: liukulukutyyppiseen muuttujaan voi asettaa kokonaisluvun, sillä Java osaa muuttaa kokonaisluvun liukuluvuksi asetuksen yhteydessä.

double liukuluku = 0.42;
liukuluku = 1; // Onnistuu

int luku = 10;
liukuluku = luku; // Onnistuu myös

Liukulukua ei kuitenkaan voi asettaa kokonaislukuun, sillä emme halua että desimaaliarvot leikkautuisivat vahingossa pois.

int luku = 4.2; // Ei onnistu

double liukuluku = 0.42;
luku = liukuluku; // Ei myöskään onnistu

Muuttujan nimentä

Muuttujan nimeämistä rajoittavat tietyt ehdot. Vaikka muuttujan nimessä voidaan käyttää ääkkösiä, on parempi olla kayttamatta niita, sillä merkistökoodauksesta saattaa tulla ongelmia.

Muuttujan nimessä ei saa olla tiettyjä erikoismerkkejä, kuten huutomerkkejä (!). Välilyönti ei ole sallittu, sillä se erottaa komentojen osat toisistaan. Välilyönti kannattaa korvata camelCase<-tyylillä, jolloin nimi muistuttaneeKamelia. Huom! Muuttujien nimien ensimmäinen kirjain kirjoitetaan aina pienellä:

int camelCaseMuuttuja = 7;

Numeroita voidaan käyttää muuttujan nimessä, kunhan nimi ei ala numerolla. Nimi ei myöskään voi koostua pelkistä numeroista.

int 7muuttuja = 4; // Ei sallittu!
int muuttuja7 = 4; // Sallittu, mutta ei kuvaava muuttujan nimi

Muuttujan nimi ei myöskään saa olla jo entuudestaan käytössä. Tälläisiä nimiä ovat mm. aikaisemmin määritellyt muuttujat ja komennot, kuten System.out.print ja System.out.println.

int camelCase = 2;
int camelCase = 5; // Ei sallittu -- muuttuja camelCase on jo käytössä!

Muuttuja kannattaa nimetä siten, että sen käyttötarkoitus on selvää ilman kommentteja tai miettimistä. Tällä kurssilla muuttujat pitää nimetä kuvaavasti.

Sallittuja muuttujien nimiä

  • kuukaudenViimeinenPaiva = 20
  • ensimmainenVuosi = 1952
  • nimi = "Essi"

Virheellisiä muuttujien nimiä

  • kuukauden viimeinen päivä = 20
  • 1paiva = 1952
  • varo! = 1910
  • 1920 = 1

Huom! Älä myöskään käytä ääkkösiä muuttujien nimissä!

Laskentaa

Laskentaoperaatiot ovat tuttuja ja suoraviivaisia: yhteenlasku +, erotus -, kertolasku * ja jakolasku /. Laskentajärjestys on myös tuttu: laskenta tehdään vasemmalta oikealle sulut huomioon ottaen. Kuitenkin * ja / lasketaan ennen + ja - operaatioita, samoin kuin perus- tai kansakoulumatematiikassa on tullut tutuksi.

Tässä vielä tarkemmin laskujärjestyksestä javassa. Linkin takana oleva materiaali ei ole kuitenkaan aloittelijan kannalta kovin oleellista.

int eka = 2;
System.out.println(eka); // tulostaa 2
int toka = 4;
System.out.println(toka); // tulostaa 4

int summa = eka + toka; // muuttujaan summa asetetaan muuttujien eka ja toka arvojen summa
System.out.println(summa); // tulostaa 6

Laskujärjestys ja sulut

Laskujärjestykseen voi vaikuttaa sulkujen avulla. Sulkujen sisällä olevat laskuoperaatiot suoritetaan ennen niiden ulkopuolella olevia laskuoperaatioita.

int laskuSuluilla = (1 + 1) + 3 * (2 + 5);
System.out.println(laskuSuluilla); // tulostaa 23

int laskuSuluitta = 1 + 1 + 3 * 2 + 5;
System.out.println(laskuSuluitta); // tulostaa 13

Yllä olevan sulkuesimerkin voi suorittaa myös askeleittain.

int laskuSuluilla = (1 + 1);
System.out.println(laskuSuluilla); // tulostaa 2
laskuSuluilla = laskuSuluilla + 3 * (2 + 5);
System.out.println(laskuSuluilla); // tulostaa 23

int laskuSuluitta = 1 + 1;
laskuSuluitta = laskuSuluitta + 3 * 2;
laskuSuluitta = laskuSuluitta + 5;
System.out.println(laskuSuluitta); // tulostaa 13

Täydennä tehtäväpohjassa olevaa ohjelmaa siten, että se laskee kuinka monta sekuntia on vuodessa. Voit olettaa, että vuodessa on 365 päivää (eli ei ole karkausvuosi).

Ohjelman tulostus on seuraava:

Vuodessa on X sekuntia.

X:n kohdalle tulee ohjelmasi laskema tulos. Huom! Hyödynnä tässä tehtävässä muuttujia :)

Lauseke ja lause

Lauseke (expression) on arvojen yhdistelmä, joka muuntuu arvoksi laskuoperaation tai evaluaation yhteydessä. Alla oleva lause sisältää lausekkeen 1 + 1 + 3 * 2 + 5, joka evaluoidaan ennen arvon asetusta muuttujaan.

int laskuSuluitta = 1 + 1 + 3 * 2 + 5;

Lausekkeen evaluaatio tapahtuu aina ennen muuttujan arvon asetusta. Tarkastele esimerkkiä alla. Jos lauseke evaluoitaisiin vasta tulostuskutsun yhteydessä, olisi tulostus jotain muuta.

int eka = 10;
int toka = eka + 5;
eka = 20;

System.out.println(toka); // tulostaa 15

Lausekkeen evaluointi tapahtuu ohjelmakoodissa siinä kohtaa, missä lauseke on. Evaluointi onnistuu siis esimerkiksi myös tulostuslauseen yhteydessä, jos lauseketta käytetään tulostuslauseen parametrin arvon laskemisessa.

int eka = 2;
int toka = 4;

System.out.println(eka + toka); // tulostaa 6
System.out.println(2 + toka - eka - toka); // tulostaa 0

Lauseke ei myöskään muuta muuttujassa olevaa arvoa, ellei lausekkeen lopputulosta aseteta muuttujan arvoksi tai anneta parametrina esimerkiksi tulostukselle.

  int eka = 2;
  int toka = 4;

  // alla oleva lauseke ei edes toimi, sillä lauseketta
  // ei aseteta minkään muuttujan arvoksi tai anneta parametrina
  // tulostuslauseelle
  eka + toka;

Merkkijonot osana laskentaa

Tarkastellaan vielä lähemmin merkkijonojen yhdistämistä +-merkinnän avulla.

Jos operaatiota + sovelletaan kahden merkkijonon välille, syntyy uusi merkkijono, jossa kaksi merkkijonoa on yhdistetty. Huomaa nokkela välilyönnin käyttö lauseen "muuttujien" osana!

String tervehdys = "Hei ";
String nimi = "Matti";
String hyvastely = ", ja näkemiin!";

String lause = tervehdys + nimi + hyvastely;

System.out.println(lause);
Hei Matti, ja näkemiin!

Jos toinen operaation + kohteista on merkkijono, muutetaan myös toinen operaation kohteista merkkijonoksi. Alla olevassa esimerkissä kokonaisluku 2 on muutettu merkkijonoksi "2", ja siihen on yhdistetty merkkijono.

System.out.println("tuossa on kokonaisluku --> " + 2);
System.out.println(2 + " <-- tuossa on kokonaisluku");
tuossa on kokonaisluku --> 2
2 <-- tuossa on kokonaisluku

Edellä esitellyt laskusäännöt pätevät täälläkin:

System.out.println("Neljä: " + (2 + 2));
System.out.println("Mutta! kaksikymmentäkaksi: " + 2 + 2);
Neljä: 4
Mutta! kaksikymmentäkaksi: 22

Edellistä soveltamalla voimme myös luoda lausekkeen, joka sisältää tekstiä ja muuttujan, ja joka evaluoidaan tulostuksen yhteydessä:

int x = 10;

System.out.println("muuttujan x arvo on: " + x);

int y = 5;
int z = 6;

System.out.println("y on " + y + " ja z on " + z);

Tulostus:

muuttujan x arvo on: 10
y on 5 ja z on 6

Tee ohjelma, jonka avulla voidaan laskea kahden kokonaisluvun summa. Ohjelman alussa määritellään kaksi muuttujaa, jotka sisältävät summattavat luvut. Voit tarvittaessa käyttää myös muita muuttujia.

Esimerkiksi jos muuttujissa on luvut 5 ja 4, ohjelman tulostus on seuraava:

5 + 4 = 9

Jos taas muuttujissa on luvut 73457 ja 12888, ohjelman tulostus on seuraava:

73457 + 12888 = 86345

Tee edellistä ohjelmaa vastaava ohjelma, joka laskee kahden kokonaislukumuuttujaan sijoitetun arvon kertolaskun.

Esimerkiksi jos muuttujissa on luvut 2 ja 8, ohjelman tulostus on seuraava:

2 * 8 = 16

Jos taas muuttujissa on luvut 277 ja 111, ohjelman tulostus on seuraava:

277 * 111 = 30747

Kokeile myös kuinka suuren kertolaskun ohjelmasi pystyy laskemaan!

Jakolasku

Kokonaislukujen jakolasku on hieman hankalampi operaatio. Jakolausekkeessa käytettyjen muuttujien tyyppi määrää evaluaation tuloksena saatavan arvon tyypin. Jos kaikki jakolausekkeessa olevat muuttujat ovat kokonaislukuja, on tulos myös kokonaisluku.

int tulos = 3 / 2;
System.out.println(tulos); // Huom! tulostaa 1 (kokonaisluku), sillä 3 ja 2 ovat myös kokonaislukuja
int eka = 3:
int toka = 2;
double tulos = eka / toka;
System.out.println(tulos); // nytkin tulostus on 1, sillä eka ja toka ovat kokonaislukuja

Jos jakolaskun jakaja tai jaettava (tai molemmat!) ovat liukulukuja, tulee tulokseksi myös liukuluku.

double kunJaettavaOnLiukuluku = 3.0 / 2;
System.out.println(kunJaettavaOnLiukuluku); // tulostaa 1.5

double kunJakajaOnLiukuluku = 3 / 2.0;
System.out.println(kunJakajaOnLiukuluku); // tulostaa 1.5

Kokonaisluku voidaan tarvittaessa muuttaa liukuluvuksi lisäämällä sen eteen tyyppimuunnosoperaatio (double):

int eka = 3;
int toka = 2;

double tulos1 = (double) eka / toka;
System.out.println(tulos1); // tulostaa 1.5

double tulos2 = eka / (double) toka;
System.out.println(tulos2); // tulostaa 1.5

double tulos3 = (double) (eka / toka);
System.out.println(tulos3); // tulostaa 1

Jälkimmäisessä esimerkissä tulos pyöristyy väärin sillä laskuoperaatio kokonaisluvuilla suoritetaan ennen tyyppimuunnosta.

Jos jakolausekkeen tulos asetetaan kokonaislukutyyppiseen muuttujaan, on tulos automaattisesti kokonaisluku.

int tulosKokonaislukuKoskaTyyppiKokonaisluku = 3.0 / 2;  // tulos automaattisesti kokonaisluku: 1

Seuraava esimerkki tulostaa "1.5", sillä jaettavasta tehdään liukuluku kertomalla se liukuluvulla (1.0 * 3 = 3.0) ennen jakolaskua.

int jaettava = 3;
int jakaja = 2;

double tulos = 1.0 * jaettava / jakaja;
System.out.println(tulos);

Tehtäväpohjassa on ohjelma, jossa on kolme muuttujaa. Ohjelman tulostus on tällä hetkellä seuraava.

eka: 9
toka: 7
kolmas: 6

Muokkaa ohjelmaa siten, että ohjelma tulostaa myös muuttujien keskiarvon.

eka: 9
toka: 7
kolmas: 6
keskiarvo: 7.333333333333333

Huom! Jos muuttujien arvoja muutetaan, tulee myös tulostuksen muuttua.

eka: 5
toka: 7
kolmas: 4
keskiarvo: 5.333333333333333

Tehtäväpohjassa on ohjelma, jossa on kaksi muuttujaa. Ohjelman tulostus on tällä hetkellä seuraava.

5 + 3 = 8

Muokkaa ohjelmaa siten, että ohjelma laskee myös lukujen erotuksen, tulon, ja jakolaskun.

5 + 3 = 8
5 - 3 = 2
5 * 3 = 15
5 / 3 = 1.6666666666666667

Huom! Jos muuttujien arvoja muutetaan, tulee myös tulostuksen muuttua.

1 + 3 = 4
1 - 3 = -2
1 * 3 = 3
1 / 3 = 0.3333333333333333

Tehtävässä ei ole testejä jotka kertovat onko tulostus oikein vai ei. Tarmista että ohjelmasi toimii oikein ennen tehtävän palautusta suorittamalla se useammalla muuttujien arvoilla.

Muuttujan arvoon liittyviä väärinkäsityksiä

Kun tietokone suorittaa ohjelmakoodia, suorittaa se sitä käsky kerrallaan, edeten aina täsmälleen siten, kuin ohjelmakoodissa sanotaan. Kun muuttujaan asetetaan arvo, tapahtuu aina sama asia, eli yhtäsuuruusmerkin oikealla puolella oleva arvo kopioidaan yhtäsuuruusmerkin vasemmalla puolella olevan muuttujan arvoksi, eli muuttujan nimeämään paikkaan.

On tärkeää, että ohjelmoija ymmärtää, että arvon asettaminen muuttujaan tekee aina saman asian.

Kolme yleistä väärinkäsitystä, jotka liittyvät muuttujan arvon asettamiseen ovat seuraavat:

  • Muuttujan asettamisen näkeminen siirtona kopioimisen sijaan: ohjelmakoodin eka = toka suorituksen jälkeen ajatellaan, että muuttujan toka arvo on siirtynyt muuttujan eka arvoksi, jonka jälkeen muuttujalla toka ei ole enää arvoa, tai sen arvo on esimerkiksi nolla. Tämä ei pidä paikkansa, sillä ohjelmakoodin eka = toka suorituksessa muuttujan toka nimeämässä paikassa oleva arvo kopioidaan muuttujan eka nimeämään paikkaan. Muuttujan toka arvo ei siis muutu.
  • Muuttujan asettamisen näkeminen riippuvuutena kopioimisen sijaan: ohjelmakoodin eka = toka suorituksen jälkeen ajatellaan, että mikä tahansa muutos muuttujaan toka vaikuttaa automaattisesti myös muuttujaan eka. Tämä ei pidä paikkansa, sillä asetus -- kopiointi -- on yksittäinen tapahtuma. Se tapahtuu vain silloin, ohjelmakoodi eka = toka suoritetaan.
  • Kolmas väärinkäsitys liittyy kopioimisen suuntaan: ohjelmakoodin eka = toka suorituksessa ajatellaan, että muuttujan toka arvoksi kopioidaan muuttujan eka arvo. Tämä näkyy myös tilanteina, missä ohjelmoija voi vahingossa kirjoittaa esimerkiksi 42 = arvo -- onneksi ohjelmointiympäristöt tukevat myös tässä.

Ehkäpä paras tapa tietokoneen ohjelmakoodin suorittamisen ymmärtämiseen on paperin ja kynän käyttäminen. Kun luet ohjelmakoodia, kirjoita paperille uusien muuttujien nimet, sekä kirjoita ylös rivi riviltä, miten ohjelmakoodissa olevien muuttujien arvot muuttuvat. Havainnollistetaan suoritusta seuraavalla ohjelmakoodilla:

rivi 1: int eka = (1 + 1);
rivi 2: int toka = eka + 3 * (2 + 5);
rivi 3:
rivi 4: eka = 5;
rivi 5:
rivi 6: int kolmas = eka + toka;
rivi 7: System.out.println(eka);
rivi 8: System.out.println(toka);
rivi 9: System.out.println(kolmas);

Alla on kirjoitettu ylläolevan ohjelmakoodin suoritus auki.

rivi 1: luodaan muuttuja eka
rivi 1: kopioidaan muuttujan eka arvoksi laskun 1 + 1 tulos
rivi 1: muuttujan eka arvo on 2

rivi 2: luodaan muuttuja toka
rivi 2: lasketaan 2 + 5, 2 + 5 ->  7
rivi 2: lasketaan 3 * 7, 3 * 7 -> 21
rivi 2: lasketaan eka + 21
rivi 2: kopioidaan muuttujan eka arvo laskuun, muuttujan eka arvo on 2
rivi 2: lasketaan 2 + 21, 2 + 21 -> 23
rivi 2: kopioidaan muuttujan toka arvoksi 23
rivi 2: muuttujan toka arvo on 23

rivi 3: (tyhjä, ei tehdä mitään)

rivi 4: kopioidaan muuttujan eka arvoksi 5
rivi 4: muuttujan eka arvo on 5

rivi 5: (tyhjä, ei tehdä mitään)

rivi 6: luodaan muuttuja kolmas
rivi 6: lasketaan eka + toka
rivi 6: kopioidaan muuttujan eka arvo laskuun, muuttujan eka arvo on 5
rivi 6: lasketaan 5 + toka
rivi 6: kopioidaan muuttujan toka arvo laskuun, muuttujan toka arvo on 23
rivi 6: lasketaan 5 + 23 -> 28
rivi 6: kopioidaan muuttujan kolmas arvoksi 28
rivi 6: muuttujan kolmas arvo on 28

rivo 7: tulostetaan muuttuja eka
rivi 7: kopioidaan muuttujan eka arvo tulostettavaksi, muuttujan eka arvo on 5
rivi 7: tulostetaan arvo 5

rivi 8: tulostetaan muuttuja toka
rivi 8: kopioidaan muuttujan toka arvo tulostettavaksi, muuttujan toka arvo on 23
rivi 8: tulostetaan arvo 23

rivi 9: tulostetaan muuttuja kolmas
rivi 9: kopioidaan muuttujan kolmas arvo tulostettavaksi, muuttujan kolmas arvo on 28
rivi 9: tulostetaan arvo 28

Alla edellinen ohjelma askeleittain visualisoituna. Käytössä oleva askeleittainen visualisointi käsittelee ohjelmakoodia riveittäin -- pohdi askeleiden kohdalla miten ohjelma päätyy sen tulostamaan lopputulokseen.

Tässä tehtävässä harjoittelet ensimmäistä kertaa tuntemattoman ohjelman muokkaamista. Vaikka suurin osa ohjelmasta tuntuu kryptiseltä, etkä tunne lähdekoodissa käytettyjä sanoja lainkaan, huomaat sieltä todennäköisesti myös tuttuja asioita.

Tehtäväpohjaan on toteutettuna graafinen (eli ohjelma avautuu ikkunassa) jakolaskin. Ohjelmassa on kuitenkin pientä häikkää, kuten alla olevasta esimerkistä huomaat.

Etsi vika ja korjaa se. Huomaa, että tässä tehtävässä (ja muissa käyttöliittymiin liittyvissä tehtävissä) automaattiset testit käyttävät hiirtäsi. Älä siis käytä hiirtäsi testien ajon aikana.

Huom! Isolla resoluutiolla varustelluissa koneissa on huomattu ongelmia hiirtä liikuttavan testikirjaston kanssa. Jos huomaat, että ohjelmasi toimii kun käynnistät sen itse ja kokeilet sen toimintaa, mutta testien ajaminen epäonnistuu, lähetä tehtävä palvelimelle arvioitavaksi.

Käyttäjän syötteen lukeminen

Seuraavaksi tutustumme käyttäjän kirjoittaman tekstin lukemiseen. Käytämme tähän Scanner-apuvälinettä. Apuväline tuodaan käyttöön lisäämällä komento import java.util.Scanner; ennen pääohjelmarungon aloitusta (public class ...).

import java.util.Scanner; // tuodaan lukemiseen käytetty apuväline Scanner käyttöömme

public class Esimerkki {

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

        // ohjelmakoodi
    }
}

Älä hätäile vaikka pääohjelmarunko saattaa näyttää vaikeaselkoiselta! Jatkamme yhä ohjelmointia kommentilla ohjelmakoodi merkittyyn kohtaan.

Merkkijonon lukeminen

Seuraava lähdekoodi lukee käyttäjän nimen ja tulostaa tervehdyksen (käyttäjän syöttämä teksti merkitty punaisella):

System.out.print("Mikä on nimesi? ");
String nimi = lukija.nextLine(); // Luetaan käyttäjältä rivi tekstiä ja
                                   // asetetaan se muuttujaan nimi

System.out.println("Hei " + nimi);
Mikä on nimesi? Venla
Hei Venla

Seuraavassa on yllä oleva ohjelma pääohjelmarungon kanssa. Ohjelman nimeksi on asetettu on Tervehdys.

import java.util.Scanner;

public class Tervehdys {

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

        System.out.print("Kenelle sanotaan hei: ");
        String nimi = lukija.nextLine(); // Luetaan käyttäjältä rivi tekstiä ja asetetaan sen arvo muuttujaan nimi

        System.out.println("Hei " + nimi);
    }
}

Kun yllä oleva ohjelma ajetaan, pääset kirjoittamaan syötteen. NetBeansin tulostusvälilehti näyttää ajetun ohjelman jälkeen seuraavalta (alla olevassa esimerkissä käyttäjä syöttää nimen "Venla").

 

Tee ohjelma joka lukee käyttäjältä merkkijonon ja tulostaa merkkijonon kolmesti.

Mikä tulostetaan? kukka

kukkakukkakukka

Esimerkissä punainen väri tarkoittaa käyttäjän kirjoittamaa tekstiä. Tätä käytäntöä noudatetaan jatkossa esimerkeissä.

Kokonaisluvun lukeminen

Scanner-apuvälineemme ei ole kovin hyvä kokonaislukujen lukemiseen, joten käytämme toista apuvälinettä merkkijonon kokonaisluvuksi muuttamisessa. Komento Integer.parseInt muuntaa sille annetussa tekstimuuttujassa olevan kokonaisluvun kokonaislukumuuttujaksi. Komennolle annetaan tekstimuuttuja parametrina, ja se palauttaa kokonaisluvun joka asetetaan kokonaislukumuuttujaan.

Käytännössä kytkemme kaksi komentoa yhteen. Ensin luemme käyttäjältä rivin, jonka annamme heti komennolle Integer.parseInt.

System.out.print("Anna kokonaisluku: ");
int kokonaisluku = Integer.parseInt(lukija.nextLine());

System.out.println("Annoit " + kokonaisluku);

Seuraavassa esimerkissä käyttäjältä kysytään ensin nimi, ja sen jälkeen ikä. Tällä kertaa esimerkissä on myös ohjelmarunko mukana.

import java.util.Scanner;

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

        System.out.print("Nimesi: ");
        String nimi = lukija.nextLine();   // Luetaan käyttäjältä rivi tekstiä

        System.out.print("Kuinka vanha olet: ");
        int ika = Integer.parseInt(lukija.nextLine()); // luetaan käyttäjältä tekstimuuttuja ja muutetaan se kokonaisluvuksi

        System.out.println("Nimesi on siis " + nimi + " ja ikäsi " + ika + ", hauska tutustua.");
    }
}

Tulostus esimerkiksi:

Nimesi: Nelli
Kuinka vanha olet: 4
Nimesi on siis Nelli ja ikäsi 4, hauska tutustua.

Lyhyt yhteenveto

Käyttäjän kanssa keskustelevan ohjelman runko:

import java.util.Scanner;

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

        // koodi tähän
    }
}

Merkkijonon lukeminen:

String merkkijono = lukija.nextLine();

Kokonaisluvun lukeminen:

int kokonaisluku = Integer.parseInt(lukija.nextLine());

Tee ohjelma, joka kysyy käyttäjältä kaksi kokonaislukua ja tulostaa niiden summan.

Anna ensimmäinen luku: 6
Anna toinen luku: 2

Lukujen summa: 8

Kuten aiemminkin, esimerkin punaisella värjätty teksti tarkoittaa käyttäjän kirjoittamaa tekstiä.

Tee ohjelma, joka kysyy käyttäjältä kaksi kokonaislukua ja tulostaa niiden osamäärän. Varmista, että 3 / 2 = 1.5. Jos desimaaliosa katoaa, lue uudelleen materiaalin kohta 3.3. Jakolasku.

Anna ensimmäinen luku: 3
Anna toinen luku: 2

Jakolasku: 3 / 2 = 1.5

Ympyrän kehän pituus lasketaan kaavalla 2 * pii * säde. Tee ohjelma, joka kysyy käyttäjältä ympyrän säteen ja laskee sen perusteella ympyrän kehän pituuden. Javasta löytyy valmis piin arvo, saat sen kirjoittamalla Math.PI laskutoimitukseen.

Anna ympyrän säde: 20

Ympyrän kehä: 125.66370614359172

Tee ohjelma, joka kysyy kahden henkilön nimet ja iät. Tämän jälkeen ohjelma tulostaa henkilöiden nimet sekä heidän ikien summan.

Kerro nimi: Charles Babbage
Kerro ikä: 226

Kerro nimi: Ada Lovelace
Kerro ikä: 202

Charles Babbage ja Ada Lovelace ovat yhteensä 428 vuotta vanhoja.

Kuka ihmeen Charles Babbage? Lue lisää Wikipediasta.

Ehtolause

Ohjelmamme ovat toistaiseksi edenneet suoraviivaisesti lauseesta seuraavaan, toimien jokaisella suorituskerralla samalla tavalla. Haluamme mahdollisuuden erilaisille ohjelman suorituspoluille. Jotta ohjelman suoritus voisi haarautua esimerkiksi käyttäjän antaman syötteen perusteella, tarvitsemme käyttöömme ehtolauseen.

int luku = 11;

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

Ehtolause alkaa avainsanalla if, jota seuraa sulut. Sulkujen sisälle asetetaan lauseke, joka evaluoidaan kun ehtolause saavutetaan. Sulkuja seuraa lohko, joka määritellään avaavan aaltosulun { ja sulkevan aaltosulun } sisään.

Jos ehtolauseen lauseke evaluoidaan todeksi, 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.

Ohjelmakoodin sisennys

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.

VäärinOikein
if (luku > 10) {
luku = 9;
}
if (luku > 10) {
    luku = 9;
}

Tee ohjelma, joka kysyy käyttäjältä kokonaisluvun ja tulostaa merkkijonon "Ylinopeussakko!" jos luku on suurempi kuin 120.

Kerro nopeus: 15
Kerro nopeus: 135

Ylinopeussakko!

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

Tee ohjelma, joka kysyy käyttäjältä kokonaisluvun ja tulostaa merkkijonon "Orwell" jos luku on täsmälleen 1984.

Anna luku: 1983
Anna luku: 1984

Orwell

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

Huom! Jos et sisennä em. tavalla, tyylitarkastaja valittaa "Line xx: '}' should be on the same line."

Tee ohjelma, joka kysyy käyttäjältä kokonaisluvun ja kertoo, onko se positiivinen (eli suurempi kuin nolla) vai ei.

Anna luku: 5

Luku on positiivinen.
Anna luku: -2

Luku ei ole positiivinen.

Tee ohjelma, joka kysyy käyttäjän ikää ja kertoo, onko tämä täysi-ikäinen (eli 18-vuotias tai vanhempi).

Kuinka vanha olet? 12

Et ole täysi-ikäinen!
Kuinka vanha olet? 32

Olet täysi-ikäinen!

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

Tee ohjelma, joka kysyy käyttäjältä kaksi kokonaislukua ja tulostaa niistä suuremman. Jos luvut ovat yhtä suuret, ohjelma huomaa myös tämän.

Esimerkkitulostuksia:

Anna ensimmäinen luku: 5
Anna toinen luku: 3

Suurempi luku: 5
Anna ensimmäinen luku: 5
Anna toinen luku: 8

Suurempi luku: 8
Anna ensimmäinen luku: 5
Anna toinen luku: 5

Luvut ovat yhtä suuret!

Vertailujen suoritusjärjestys

Vertailut suoritetaan järjestyksessä ylhäältä alaspäin. Kun suorituksessa päästään lohkoon, 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.");
}
Luku on suurempi kuin nolla.

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

Tee ohjelma, joka ilmoittaa kurssiarvosanan seuraavan taulukon mukaisesti.

pistemäärä arvosana
< 0 mahdotonta!
0-50 hylätty
51-60 1
61-70 2
71-80 3
81-90 4
91-100 5
> 100 uskomatonta!

Esimerkkitulostuksia:

Anna pisteet [0-100]: 37

Arvosana: hylätty
Anna pisteet [0-100]: 76

Arvosana: 3
Anna pisteet [0-100]: 95

Arvosana: 5
Anna pisteet [0-100]: -3

Arvosana: mahdotonta!

Tehtävän graafiseen käyttöliittymään on lisätty liukuri sekä tekstikenttä.

Tutustu ohjelman koodiin ja muokkaa ohjelmaa siten, että tekstikentän arvo muuttuu liukurista saatavien arvojen perusteella seuraavasti:

  • Jos liukurin arvo on 0, tekstin tulee olla "Ei lainkaan."
  • Jos liukurin arvo on 1-25, tekstin tulee olla "Vähän."
  • Jos liukurin arvo on 26-74, tekstin tulee olla "Kohtalaisesti."
  • Jos liukurin arvo on 75-99, tekstin tulee olla "Paljon."
  • Jos liukurin arvo on 100, tekstin tulee olla "Kaikki."

Huomaa tässäkin tehtävässä, ettei sinun tarvitse ymmärtää ohjelmakoodista kuin vain muutamia oleellisia kohtia.

Loogiset operaatiot

Ehtolauseen lauseke voi olla myös monimutkaisempi, yksittäisten loogisten operaatioiden avulla koostettu ehto. Loogisia operaatioita ovat:

  • lauseke1 && lauseke2 lausekkeen arvo on tosi kun molemmat ehdoista ovat tosia
  • lauseke1 || lauseke2 lausekkeen arvo on tosi jos jompi kumpi tai molemmat ehdoista tosia

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ää jomman kumman 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! :)

Tee ohjelma, joka kysyy käyttäjän iän ja tarkistaa, että se on mahdollinen (ainakin 0 ja korkeintaan 120).

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

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);
Totuusarvomuuttujan arvo on true

Ehtolauseen voi suorittaa myös seuraavasti:

boolean onkoTotta = true;

if (onkoTotta) {
    System.out.println("Aika vinhaa!");
}
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:

1 on pienempi kuin 3!
Jakojäännös

Jakojäännös on hieman harvemmin käytetty operaatio, joka on kuitenkin varsin näppärä kun halutaan tarkistaa esimerkiksi luvun jaollisuutta. Jakojäännösoperaation merkki on %.

int jakojaannos = 7 % 2;
System.out.println(jakojaannos); // tulostaa 1

System.out.println(5 % 3); // tulostaa 2
System.out.println(7 % 4); // tulostaa 3
System.out.println(8 % 4); // tulostaa 0
System.out.println(1 % 2); // tulostaa 1

Jos haluamme tietää onko käyttäjän syöttämä luku jaollinen neljälläsadalla, tarkastamme onko syötetyn luvun jakojäännös neljänsadan suhteen nolla.

Scanner lukija = new Scanner(System.in);
int luku = Integer.parseInt(lukija.nextLine());
int jakojaannos = luku % 400;

if (jakojaannos == 0) {
    System.out.println("Luku " + luku + " on jaollinen neljälläsadalla.");
} else {
    System.out.println("Luku " + luku + " ei ole jaollinen neljälläsadalla.");
}

Koska jakojäännös on samanlainen operaatio kuin muutkin laskut, voi sen asettaa osaksi valintakäskyä.

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

if (luku % 400 == 0) {
    System.out.println("Luku " + luku + " on jaollinen neljälläsadalla.");
} else {
    System.out.println("Luku " + luku + " ei ole jaollinen neljälläsadalla.");
}

Tee ohjelma, joka kysyy käyttäjältä luvun ja ilmoittaa, onko syötetty luku parillinen vai pariton.

Anna luku: 2
Luku 2 on parillinen.
Anna luku: 7
Luku 7 on pariton.

Vihje: Luvun jakojäännös 2:lla kertoo, onko luku parillinen vai pariton. Jakojäännos taas saadaan %-operaattorilla, tehtäväpohjassa on lisää ohjeita miten parittomuustarkastus hoituu jakojäännöksen avulla.

Ehtolauseista ja lohkoista

Monimutkaisempien ehtolauseiden muodostamisessa voi hyödyntää sulkuja:

int luku = 99;

if ((luku > 0 && luku < 10) || luku > 100) {
    System.out.println("luku oli joko yhden ja yhdeksän väliltä tai yli sata");
} else {
    System.out.println("luku oli 0 tai pienempi tai väliltä 10-99");
}
  luku oli 0 tai pienempi tai väliltä 10-99

Yllä oleva esimerkki toimii käytännössä samoin kuin seuraava esimerkki.

int luku = 99;

if (luku > 0 && luku < 10) {
    System.out.println("luku oli joko yhden ja yhdeksän väliltä tai yli sata");
} else if (luku > 100) {
    System.out.println("luku oli joko yhden ja yhdeksän väliltä tai yli sata");
} else {
    System.out.println("luku oli 0 tai pienempi tai väliltä 10-99");
}

Ehtolauseen lohkon sisällä voi olla mitä tahansa koodia, myös toinen ehtolause.

int x = 45;
int luku = 55;

if (luku > 0) {
    System.out.println("Luku on positiivinen");

    if (luku > x) {
        System.out.println(" ja suurempi kuin muuttujan x arvo");
        System.out.println("muuttujan x arvohan on " + x);
    }
}

Merkkijonojen vertailu

Yhtäsuuruusoperaattorilla == ja erisuuruusoperaattorilla != tutkitaan kahden muuttujan arvo samuutta. Tämä toimii esimerkiksi kokonaisluvuilla ja oikeastaan kaikilla muuttujatyypeille, joiden tyyppi alkaa pienellä alkukirjaimella. Merkkijonojen tapauksessa tilanne on kuitenkin toinen.

Tarkastellaan alla olevaa esimerkkiohjelmaa.

import java.util.Scanner;

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

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

        if (eka != toka) {
            System.out.println("Syötetyt merkkijonot eivät olleet samat!");
        }
    }
}
  Syötä kaksi merkkijonoa.
  merkkijono
  merkkijono
  Syötetyt merkkijonot eivät olleet samat!

Merkkijonoja, eli tekstejä, ei siis voi vertailla yhtäsuuruusoperaatiolla.

Merkkijonojen vertailuun käytetään erillistä equals-komentoa, joka liittyy aina verrattavaan merkkijonoon.

String teksti = "kurssi";

if (teksti.equals("marsipaani")) {
    System.out.println("Teksti-muuttujassa on teksti marsipaani.");
} else {
    System.out.println("Teksti-muuttujassa ei ole tekstiä marsipaani.");
}

Komento equals liitetään aina siihen verrattavaan tekstimuuttujaan, "tekstimuuttuja piste equals teksti". Tekstimuuttujaa voidaan myös verrata toiseen tekstimuuttujaan.

String teksti = "kurssi";
String toinenTeksti = "pursi";

if (teksti.equals(toinenTeksti)) {
    System.out.println("Samat tekstit!");
} else {
    System.out.println("Ei samat tekstit!");
}

Merkkijonoja vertailtaessa on syytä varmistaa että verrattavalla tekstimuuttujalla on arvo. Jos muuttujalla ei ole arvoa, ohjelma tuottaa virheen NullPointerException, joka tarkoittaa ettei muuttujan arvoa ole asetettu tai se on tyhjä (null).

Seuraavassa käännetään !:n eli negaatio-operaation avulla ehdon arvo päinvastaiseksi:

System.out.println("Eihän merkkijono ole 'maito'");
String merkkijono = "piimä";

if (!(merkkijono.equals("maito"))) {  // tosi jos ehto merkkijono.equals("maito") on epätosi
    System.out.println("ei ollut!");
} else {
    System.out.println("oli");
}
ei ollut!

Negaatio-operaatio, eli !ehto, kääntää siis totuusarvon ympäri.

int eka = 1;
int toka = 3;

boolean onkoSuurempi = eka > toka;

if (!onkoSuurempi) {
    System.out.println("1 ei ole suurempi kuin 3");
}
1 ei ole suurempi kuin 3

Tämä ilmiö selvenee myöhemmin kurssilla.

Tehtäväpohjassa on yritetty luoda ohjelma kahden merkkijonon vertailuun. Lähdekoodi on tällä hetkellä seuraava:

import java.util.Scanner;

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

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

        if (eka == toka) {
            System.out.println("Syötetyt merkkijonot olivat samat!");
        } else {
            System.out.println("Syötetyt merkkijonot eivät olleet samat!");
        }
    }
}

Ohjelma ei kuitenkaan toimi halutulla tavalla. Korjaa se.

Tee ohjelma, joka pyytää käyttäjää kirjoittamaan merkkijonon. Jos käyttäjä kirjoittaa merkkijonon "totta", tulostetaan merkkijono "Oikein meni!", muulloin tulostetaan merkkijono "Koitappa uudelleen!".

Kirjoita merkkijono: totta
Oikein meni!
Kirjoita merkkijono: tottapa
Koitappa uudelleen!

Tee ohjelma, joka tunnistaa seuraavat käyttäjät:

tunnus salasana
aleksi tappara
elina kissa

Ohjelma näyttää käyttäjälle henkilökohtaisen viestin tai ilmoittaa, jos tunnus tai salasana on väärin.

Anna tunnus: aleksi
Anna salasana: tappara
Hei aleksi, olet kirjautunut järjestelmään
Anna tunnus: elina
Anna salasana: kissa
Hei elina, olet kirjautunut järjestelmään
Anna tunnus: aleksi
Anna salasana: jokerit
Virheellinen tunnus tai salasana!

Virhetilanteet ja ongelman ratkaiseminen askel kerrallaan

Ohjelmia luodessa niihin päätyy virheitä. Joskus virheet eivät ole niin vakavia, ja aiheuttavat päänvaivaa lähinnä ohjelman käyttäjälle, mutta joskus virheet voivat johtaa hyvinkin vakaviin seurauksiin. Varmaa on joka tapauksessa se, että ohjelmoimaan opetteleva ihminen tekee paljon virheitä.

Tätä ei kuitenkaan kannata missään nimessä pelätä tai välttää, sillä virheitä tekemällä oppii parhaiten. Pyri siis myös välillä rikkomaan työstämääsi ohjelmaa, jolloin pääset tutkimaan virheilmoitusta ja tarkastelemaan kertooko virheilmoitus jotain tekemästäsi virheestä.

Ohjelmistovirhe

Osoitteessa http://sunnyday.mit.edu/accidents/MCO_report.pdf oleva raportti kuvaa erään hieman vakavamman ohjelmistovirheestä johtuneen tapaturman, sekä ohjelmistovirheen.

Ohjelmistovirhe liittyi siihen, että käytetty ohjelma odotti, että ohjelmoija käyttäisi kansainvälistä yksikköjärjestelmää laskuissa (metrit, kilogrammat, ...). Ohjelmoija oli kuitenkin käyttänyt amerikkalaista mittajärjestelmää erään järjestelmän osan laskuissa, jolloin satelliitin navigointiin liittynyt automaattinen korjausjärjestelmä ei toiminut oikein.

Satelliitti tuhoutui.

Ohjelmien muuttuessa monimutkaisemmiksi, tulee virheiden löytämisestäkin haastavampaa. NetBeansiin integroitu debuggeri voi olla avuksi virheiden löytämisessä. Seuraavalla screencastilla esitellään debuggerin käyttöä. Screencast esittelee myös miten projekteja voidaan luoda, avata ja sulkea sekä miten ohjelmia voidaan suorittaa NetBeansin ulkopuolella. Screencastissa on myös asioita, joita kurssilla ei vielä ole tullut -- älä huoli, nämä tulevat vastaan opintojen edetessä.

Stack trace

Kun ohjelmassa tapahtuu virhe, se tyypillisesti tulostaa ns. stack tracen, eli niiden metodikutsujen listan, joiden seurauksena virhetilanne syntyi. Stack trace voi näyttää esimerkiksi seuraavalta:

Exception in thread "main" ...
        at Ohjelma.main(Ohjelma.java:15)

Listan alussa kerrotaan minkälainen virhe tapahtui (tässä ...), ja seuraavalla rivillä kerrotaan missä virhe tapahtui. Rivi "at Ohjelma.main(Ohjelma.java:15)" sanoo, että virhe tapahtui Ohjelma.java-tiedoston rivillä 15.

        at Ohjelma.main(Ohjelma.java:15)

Muistilista virheenselvitykseen

Jos koodisi ei toimi etkä tiedä missä on virhe, näillä askeleilla pääset alkuun.

  1. Sisennä koodisi oikein ja selvitä, puuttuuko sulkuja.
  2. Tarkista ovatko käytetyt muuttujat oikean nimisiä.
  3. Testaa ohjelman kulkua erilaisilla syötteillä, ja selvitä minkälaisella syötteellä ohjelma ei toimi halutusti. Jos sait virheen testeistä, testit saattavat myös kertoa käytetyn syötteen.
  4. Lisää ohjelmaan tulostuskomentoja, joissa tulostat käytettävien muuttujien arvoja ohjelman suorituksen eri vaiheissa.
  5. Tarkista, että kaikki käyttämäsi muuttujat on alustettu. Jos tätä ei ole tehty, seuraa virhe NullPointerException.
  6. Jos ohjelmasi aiheuttaa poikkeuksen, kannattaa ehdottomasti kiinnittää huomiota poikkeuksen yhteydessä olevaan stack traceen, eli niiden metodikutsujen listaan, minkä seurauksena poikkeuksen aiheuttanut tilanne syntyi.
  7. Opettele käyttämään debuggeria, aiemmin nähdyllä videolla pääsee alkuun.

Ongelman ratkaiseminen osa kerrallaan

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ä kolmellä 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. Miksi ei? Kokeile itse!

Scanner lukija = new Scanner(System.in);

int luku = Integer.parseInt(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);
}

Alla kerrotaan syy edellisen ohjelman toimimattomuuteen.

Valintakäskyjen suoritusjärjestyksestä

Edellisessä lähestymistavassa ongelmana on se, että valintakäskyjen 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ä ja tulostaa sen.
  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".

Nyt ongelmakin tuntuu ratkeavan.

Scanner lukija = new Scanner(System.in);

int luku = Integer.parseInt(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);
}

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.

Toteuta laskin, joka tarjoaa plus, miinus, kerto sekä jakolaskutoiminnallisuuden. Alla on kuvattu laskimelta toivottu toiminallisuus.

    Anna ensimmäinen luku: 7
    Anna toinen luku: 1
    Anna operaatio: +

    7 + 1 = 8
    Anna ensimmäinen luku: 5
    Anna toinen luku: 3
    Anna operaatio: -

    5 - 3 = 2
    Anna ensimmäinen luku: 22
    Anna toinen luku: 3
    Anna operaatio: *

    22 * 3 = 66
    Anna ensimmäinen luku: 17
    Anna toinen luku: 3
    Anna operaatio: /

    17 / 3 = 5.666666666666667

Osan viimeisessä tehtävässä pääset taas tarkastelemaan melko tuntematonta lähdekoodia. Tehtävässä on valmiiksi toteutettuna ohjelma, missä hymiö seuraa hiirtä.

Tehtävänäsi on muokata ohjelmaa siten, että hymiö ei koskaan osu hiireen. Kun saat hiirtä välttävän ohjelman tehtyä, voit halutessasi alkaa miettimään miten hymiö voisi vältellä hiirtä.

Lähdekoodissa on valmiina muuttujia, joiden avulla voit tarkastella hiiren ja hymiön nykyistä ja edellistä sijaintia. Näistä lienee hyötyä. Saat esimerkiksi laskettua hiiren liikkeen x-akselilla muuttujien vanhaHiirenX ja hiirenX avulla. Voit myös selvittää tämän tiedon avulla kulkeeko hiiri hymiötä kohti.

Tehtävässä ei ole erillisiä automaattisia testejä. Palauta tehtävä kun olet saavuttanut tavoitteen -- palautukseksi kelpaa siis myös ohjelma, missä hiirellä ei koskaan osuta hymiöön.

Ensimmäinen osio päättyy tähän. Takanasi on jo ainakin 30 erilaista ohjelmaa -- jos osa on jäänyt epäselviksi, kannattaa niihin palata myöhemmin kurssilla.

Sisällysluettelo