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)
lause
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 (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ä.
Takaisin luvun 3 sisällysluetteloon.