Helsingin yliopisto / Tietojenkäsittelytieteen laitos / 581258-1 Johdatus ohjelmointiin
Copyright © 2005 Arto Wikla. Tämän oppimateriaalin käyttö on sallittu vain yksityishenkilöille opiskelutarkoituksissa. Materiaalin käyttö muihin tarkoituksiin, kuten kaupallisilla tai muilla kursseilla, on kielletty.

3.4 Lauseita

(Muutettu viimeksi 5.11.2009)

Kuten edellisen luvun alussa todettiin, lauseke (expression) on ohjelmointikielen ilmaus, jolla on jokin arvo ja tuolla arvolla on jokin tyyppi. Lause (statement) puolestaan ilmaisee jonkin algoritmisen toiminnon, "tehdään jotakin"... Javassa on lisäksi lausekelauseita (expression statements), joita voi käyttää sekä lausekkeen, että lauseen tapaan. Laskettua arvoa ei jälkimmäisessä tapauksessa käytetä mihinkään. Laskennalla voi kuitenkin olla haluttuja (sivu-)vaikutuksia.

Ns. rakenteisten lauseiden tehtävä on valita suoritettavia lauseita, toistaa lauseita, ... Ne ovat rakenteisia, juuri siksi, että niiden rakenneosina on lauseita.

Yksinkertaiset lauseet päättyvät aina puolipisteeseen. Rakenteisilta lauseilta tätä ei vaadita. Sallittua se silti on, koska Javassa on ns. tyhjä lause.

Tämä luku esittelee tiiviisti useimmat Javan lauseet.

Lohkot ja muuttujien määrittelylauseet

Lohko on jono lauseita merkkien "{" ja "}" välissä. Lohkossa voi olla muiden lausetyyppien lisäksi paikallisten muuttujien määrittelyitä. Tällaiset määrittelyt ovat voimassa vain kyseisessä lohkossa ja vain määrittelykohdasta eteenpäin. Muuttujien määrittelyt voivat siis sijaita vapaasti muiden lauseiden joukossa! Muuttujien määrittelyt ovat suoritettavia lauseita: mahdollisen alkuarvon laskeminen ja sijoittaminen muuttujaan tehdään tavallisen sijoitusoperaation tapaan. Määrittelyt suoritetaan vasemmalta oikealle.
{ int a = 1, b, c = 2*a;
  b = a * 2;
  int d = a + b;   // d on käytössä vasta tästä alkaen!!
  { int e = d + b; // e on käytössä tästä alkaen
    d = a + e;
  }                // e:n käyttöalue loppuu tähän
  c = d + b;
}
Huom: Tavallisesti on toki selkeintä määritellä lohkon muuttujat lohkon alussa! Toisaalta periaate "määrittele mahdollisimman syvällä, mahdollisimman myöhään" on myös perusteltavissa. Miksi?

Huomattavaa:

Esimerkkejä muuttujien määrittelyistä (Esimerkit havainnollistavat määrittelyiden monia mahdollisuuksia, älkööt ne kuitenkaan ketään innostako vaikeaselkoisuuteen!):

long i;
int j = 1, k = 2, m, n = 0;

int eka=1, toka=eka+1, kolm=eka+2, nel=kolm+1;

double dd = 6.0, ee = dd*3.14;
float  ff = 3.21F;  // Liukulukuvakiot ovat
         // ----^!     oletusarvoisesti double-tyyppisiä!

int    ii = (int)ee, jj = '2';
char   cc = '#', bb = (char)dd;

String gg ="Miau!", hh = "tulos on "+ (ff+dd);
String nnn = 2 + 3 + "böö";   //nnn:n arvoksi tulee "5böö"
String mmm = 2 + (3 + "böö"); //mmm:n arvoksi tulee "23böö"

Pikkuvarasto rr, pp = null, qq = new Pikkuvarasto(10.0, "Mehu"); 

Metodin muuttujilla ei ole mitään oletusalkuarvoja (luokassa määritellyillä ilmentymä- ja luokkamuuttujilla sellaiset sitävastoin on, tähän palataan luvussa 4.2).

Kääntäjä tarkistaa, ettei metodissa käytetä arvon antajana - esimerkiksi sijoitusoperaation oikena puolena tai tulostettavana - sellaista muuttujaa, jolle ei ole asetettu alkuarvoa. Kääntäjä tekee ns. tietovirta-analyysin (data flow analysis) hyvin epäluuloisena: Jos tuollaista muuttujaa jossain tilanteessa edes saatettaisiin käyttää, kääntäjä ei anna armoa!

Huom: Koska määrittely on suoritettava lause, alkuarvon antava lauseke lasketaan kaikkine sivuvaikutuksineen joka suorituskerralla:

int a = 1;
for (int i=1; i<=3; ++i) {
  Pikkuvarasto c = new Pikkuvarasto(i*10.0, "Mehu");
  int b = ++a;

  System.out.println(b);
  // ...
}
Joka toistokerralla luodaan uusi Pikkuvarasto-olio ja ohjelmanpätkä tulostaa luvut 2, 3 ja 4.

Lausekelauseet

Eräitä lausekkeita voi käyttää lauseiden tapaan. Lausekkeen sivuvaikutukset tekevät jotakin (hyödyllistä?) ja lausekkeen varsinainen arvo jätetään kylmästi käyttämättä. Lauseke kirjoitetaan sellaisenaan ja päätetään puolipisteeseen.

Vain seuraavia lausekkeita voi käyttää lauseina:

Huom: Siis lauseen tapaan käyttämämme sijoitusoperaatio ja yhdellä kasvatus/vähennys on Javassa todellakin teknisesti toteutettu lausekkeena - arvon ilmauksena - jota vai saa käyttää lauseena. Useimmiten on selkeintä käyttää näitä nimenomaan lauseina!

Huom: Muita lausekkeita ei siis voi käyttää lauseina!

Esimerkkejä:

  i = 1;
  j = i + 4;

  ++i; // Kun 1:llä kasvatus- tai vähennyslauseketta 
  j++; // käytetään lauseena, järjestyksellä ei ole väliä!

  mehua.otaVarastosta(15); //vaikka:
                           // public double otaVarastosta(double)
                           //        ^^^^^^

  new Pikkuvarasto(); // konstruktorin suoritus voi joskus
                      // olla ainoa, mitä halutaan

Ehdolliset lauseet if ja if-else

  if (ehto)
    lause


  if (ehto)
    lause1
  else
    lause2

Valintalause switch

  switch (lauseke) {
    case vakio1: lause1;
                 break;
    case vakio2: case vakio3:
                 lause2;
                 lause3;
                 break;
    default:     lause4;
                 break;
  }

Ehdolliset toistolauseet while ja do

  while (ehto) 
    lause


  do
    lause
  while (ehto);

For-toisto

  for (alustuslauseke; ehto; kasvatuslauseke)
    lause

For-lauseen mahdolliset laskentajonot, "suoritushistoriat", ovat:

For-lause vastaa rakennetta:

  {
    alustuslauseke;
    while (ehto) {
      lause
      kasvatuslauseke;
    }
  }
(Paitsi: continue-lauseella keskeytetty toisto suorittaa kasvatuslausekkeen ennen ehdon uutta testausta; continue-lause käsitellään seuraavana.)

For-lause voidaan nähdä "toiston yleisenä neliparametrisena rakenteena, johon kuuluu alkutoimet, päättöehto, päivitystoimet (kunkin toiston jälkeen) ja toistettava" (Tomi Silander).

For-each-toisto

[Seuraava teksti on lopun huomautukseen saakka kopio luvun 2 vastaavasta lisäyksestä.]

Javan versio 1.5 toi mukanaan uuden tavan käydä läpi taulukon kaikki alkiot ilman tarvetta itse indeksoida. For-lauseen uusi syntaksivaihtoehto on

    for (alkio : taulukko)
      käsittele alkio;
Lause tarkoittaa sitä, että yksi kerrallaan käydään läpi kaikki taulukon alkiot ja toistettava alialgoritmi saa yksitellen käyttöönsä kaikkien niiden arvot muuttujassa alkio. Toistettavalla alialgoritmilla ei ole mitään tietoa alkion indeksistä, vain arvosta. Alialgoritmi ei myöskään pääse muuttamaan taulukon alkioiden arvoja! Kuten arvata saattaa, tämä muoto for-lauseesta soveltuu juuri tilanteisiin, joissa mitään tietoa alkion indeksistä ei tarvita!

Tällaisella semantiikalla varustettua for-toistoa kutsutaan joissakin ohjelmointikielissä for-each-toistoksi ilmeisestä syystä. Javassa tällainen on käytettävissä talukon kaikkien alkioiden läpikäynnin lisäksi myös ns. kokoelmatyypeille (Collections). Uusi for-lause käy niissäkin yksitellen läpi kaikki tietorakenteen alkiot.

Esimerkki: Ohjelmoidaan metodi, joka laskee int-taulukon alkioiden summan ja palauttaa sen arvonaan. Summaa laskettaessa ei ole kiinnostavaa, missä järjestyksessä lasketaan eikä ole kiinnostavaa, mikä kunkin alkion indeksi on. Tämä on siis varsin oivallinen tilanne kayttää for-each-toistoa! (LaskeSumma.java)

  private static int alkiodenSumma(int[] taulukko) {
    int summa = 0;
    for (int alkio : taulukko)
       summa = summa + alkio;
    return summa;
  }

Huom: Itse asiassa for-each-lauseella voidaan taulukon alkioiden lisäksi käydä läpi ns. kokoelmien (Collections) alkioita. Myös luetellun tyypin arvoalue voidaan käydä läpi:

  public enum Arvo { KAKKONEN, KOLMONEN, NELONEN, VITONEN,
                     KUTONEN, SEISKA, KASI, YSI, KYMPPI,
                     JÄTKÄ, ROUVA, KUNKKU, ÄSSÄ }

  public enum Maa  { RISTI, HERTTA, PATA, RUUTU }
  ...

      for (Maa maa : Maa.values())
        for (Arvo arvo : Arvo.values())
          System.out.println("" + maa + arvo);


Keskeytyslauseet break, continue ja return

Keskeytyslauseella keskeytetään rakenteisen lauseen suoritus: Javassa lausetta voi edeltää osoite. Break- ja continue-lauseilla voi rakenteisen lauseen osoitteeseen viittamaalla keskeyttää myös ulompien tasojen lauseita:
  ulompi: while (jotakin) {
            int i = 1;
            ...
  sisempi:  while (jotain muuta) {
              int a = 1;
              ...
              if (meni vähän pieleen)
                break sisempi;
              ...
              if (kaikki meni pieleen)
                break ulompi;
              ...
            }
          }

Keskeytyslauseet voivat joskus olla näppäriä esimerkiksi virhetilanteiden hoitamisessa. Ohjelman normaalin etenemisen ohjaamisessa niitä on syytä käyttää harkiten.

Muita lauseita

Ns. Poikkeukset (exceptions) ovat "poikkeuksellisia tilanteita" kesken normaalin ohjelmansuorituksen: tiedosto loppuu, merkkijono ei kelpaa kokonaisluvuksi, odotetun olion tilalla onkin null-arvo, taulukon indeksi menee ohjelmointivirheen takia sopimattomaksi, ... Poikkeustenkäsittelyvälineitä, try-catch- ja throw-lauseita, nähdään luvussa 5 tiedostojen käsittelyn yhteydessä.

Ns. rinnakkaisohjelmointia ("threadit") ja siihen liittyviä lauseita ei tällä kurssilla käsitellä.


Takaisin luvun 3 sisällysluetteloon.