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.
{ 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:
{ //... { int i; //... } { int i; //... } int i; //... }Metodin muuttujalla ei missään ohjelmakohdassa saa olla kahta merkitystä.
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");Metodeissa muuttujilla ei ole mitään oletusalkuarvoja (luokkien muuttujilla 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.
Vain seuraavia lausekkeita 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
if (ehto) lause if (ehto) lause1 else lause2
if (a<b) {if (c<d) e = f; } else g=h;Ilman tuota lohkoa else-osa liittyisi sisempään if-lauseeseen. (Sisentelystä riippumatta.)
switch (lauseke) { case vakio1: lause1; break; case vakio2: case vakio3: lause2; lause3; break; default: lause4; break; }
//lähtölaskenta (korkeintaan 3:sta) int lkm; // ... switch (lkm) { case 3: System.out.print("kolme, "); case 2: System.out.print("kaksi, "); case 1: System.out.print("yksi, "); case 0: System.out.print("nolla: "); } System.out.println("PUM!");Jos muuttujan lkm arvo on 3, ohjelmanpätkä tulostaa:
kolme, kaksi, yksi, nolla: PUM!
Esimerkki (Komentotulkki.java):
public class Komentotulkki { private static void teeA() { // ... } private static void teeB() { // ... } private static void teeC() { // ... } private static void teeD() { // ... } public static void main(String[] args) { char komento; do { komento = Lue.merkki(); switch (komento) { case 'a': teeA(); break; case 'b': teeB(); break; case 'c': teeC(); break; case 'd': teeD(); break; case 'l': break; // LOPETUSKOMENTO ON 'l' default: System.out.println("Virheellinen komento!"); break; } } while (komento!='l'); } }
while (ehto) lause do lause while (ehto);
for (alustuslauseke; ehto; kasvatuslauseke) lauseFor-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 (int i=1, j=20; i<j; i+=2, j-=3) System.out.println(i + " " + j);Ohjelmanpätkä tulostaa:
1 20 3 17 5 14 7 11
while (jotakin) { ... if (keskeytyttää) break; ... }
On kauniimpiakin tapoja tulostaa parittomat välillä 1-10 kuin seuraava:
for (int i=1; i<10; ++i) { if (i%2 == 0) continue; System.out.println(i); }
public static boolean intToBool(int luku) { return luku != 0; }Return-lauseita voi olla metodissa useampiakin. Jokaisessa arvon palauttavassa metodissa on oltava ainakin yksi saavutettavissa oleva return-lause palautusarvoineen.
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.
Ns. rinnakkaisohjelmointia ("threadit") ja synchronized-lausetta ei tällä kurssilla käsitellä.