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.
Tässä luvussa käsitellään vain alkeistyyppisiä lausekkeita. Viittaustyyppisten arvojen, olioiden, käsittelemistä opitaan muissa luvuissa. Javassa on myös bittijonoa muokkaavia operaatioita, mutta niitä ei kurssilla käsitellä eikä käytetä!
Javalla on mahdollista laatia selkeitä ja ymmärrettäviä lausekkeita, mutta kovasti yrittämällä voi kirjoittaa myös hyvin vaikeaselkoisia. Arvatkaapa kumpia arvostetaan enemmän kokeessa, elämässä, ...?
(Tarkka selvitys tästä asiasta ja muunnoksen "turvallisuudesta": The Java Language Specification: 5.1.2 Widening Primitive Conversions [Sunin sivu])
Sääntöjä:
Esimerkki:
short a = -23; int b = 123; long c = 121234; char d = 'q'; float e = 3.14f; double f = 121.2311; //sallittuja kiellettyjä b = a; a = b; c = b; b = c; f = a; a = f; f = e; e = f; b = d; d = b; a = d; d = a; f = d; a = 8-0 // kokonaislukulaskutoimitukset tehdään // aina vähintään int-tyyppisinä!!Kääntäjä antaa seuraavantapaisia ilmoituksia (4. virheellinen tapaus):
Incompatible type for =. Explicit cast needed to convert double to float. e = f; ^Eksplisiittinen tyyppimuunnos (cast) ilmaistaan kirjoittamalla arvon saajan tyyppinimi sulkeisiin lausekkeen eteen:
short a = -23; int b = 123; long c = 121234; char d = 'q'; float e = 3.14f; double f = 121.2311; a = (short)b; b = (int)c; a = (short)f; e = (float)f; d = (char)b; a = (short)d; d = (char)a; a = (short)(8-0); a = (short)(3.14 * 1000.3 - 0.00002); // huom. sulkeet lausekkeen ympärillä!!Järkevillä tapauksilla on järkevä tulkinta: liukuluku "pyöristetään (kohti nollaa)" kokonaisluvuksi, kyseessä on siis katkaisu! Kun double-arvosta tehdään float, esitystarkkuus voi pienetä; sopivankokoinen kokonaisluku voidaan muuttaa char-arvoksi, ... (tarkemmin: spesifikaatio 5.1.3 [Sunin sivu])
Järjettömissä tapauksissa sattuu järjettömyyksiä, joista ohjelmoija kantaa vastuun!
Huom: boolean-tyyppistä lauseketta ei voi muuntaa numeeriseksi edes eksplisiittisellä tyyppimuunnoksella!
Kaikille numeerisille tyypeille on käytössä operaatiot:
+ - * / %Jos molemmat operandit (so. laskettavat) ovat kokonaislukutyyppisiä, myös laskutoimituksen tulos on on kokonaisluku (aina vain joko int tai long - operandien tyypistä riippuen; jos arvo sijoitetaan byte- tai short-muuttujaan, on tehtävä eksplisiittinen tyyppimuunnos!).
Operaatio "/" tarkoittaa kokonaisjakoa, jos sekä osoittaja että nimittäjä ovat jotain kokonaislukutyyppiä. Aina muulloin kyseessä on liukulukujakolasku.
Operaatio "%" on jäännösjako. Tuloksen etumerkki on sama kuin osoittajan etumerkki, nimittäjän etumerkillä ei ole vaikutusta tuloksen etumerkkiin. Jäännösjaon soveltaminen liukuluvuille on sallittua, mutta ei järkevää! (Ks. spesifikaatio 15.16.3.)
Huom: Jos kahden kokonaislukuarvon jakamisen tuloksena halutaan liukuluku, on se erikseen ilmaistava. Esimerkiksi ohjelma:
public class koe { public static void main(String[] args) { int a=5, b=2; System.out.println( a/b ); System.out.println( a/b * 1.0 ); System.out.println( 1.0 * a/b ); System.out.println( (float)a/b ); System.out.println( (float)(a/b) ); System.out.println( (a/(float)b) ); } }tulostaa
2 2.0 2.5 2.5 2.0 2.5
Huom: Operaatio "+" tarkoittaa merkkijonojen katenointia (l. yhteenliittämistä) jos edes toinen 'yhteenlaskettavista' on String-olio (laskenta etenee vasemmalta oikealle). Esimerkki:
int i=2, j=5; System.out.println("Summa on " + i+j); // tulostaa: Summa on 25 System.out.println("Summa on " + (i+j)); // tulostaa: Summa on 7 System.out.println(i+j + " on summa"); // tulostaa: 7 on summa System.out.println(i+ (j + " on summa")); // tulostaa: 25 on summa
Vakioita:
Math.E // luku e (2.7182818284590452354) Math.PI // luku pii (3.14159265358979323846)Funktioita (monia muitakin on!):
Math.abs // itseisarvo Math.sin Math.cos Math.tan Math.exp Math.log // luonnollinen logaritmi Math.sqrt Math.pow // 1. double potenssiin 2. double Math.round Math.random() // double-pseudosatunnaisluku väliltä [0.0, 1.0) Math.max Math.minJotkin näistä on kuormitettu eri tyyppisille parametreille. (Tarkka kuvaus löytyy kielen määrittelyn kohdasta 20.11 The Class java.lang.Math [Sunin sivu])
Esimerkki:
for (double d=0.0; d < 2*Math.PI; d+=0.1) System.out.println(Math.sin(d));
++muuttuja muuttuja++ --muuttuja muuttuja--Operaatio kasvattaa (vähentää) muuttujan arvoa yhdellä.
On siis mahdollista ohjelmoida:
int i=0; while (++i < 10) System.out.print(i+" "); System.out.println(); i=0; while (i++ < 10) System.out.print(i+" "); System.out.println();Lauseet tulostavat:
1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 10Huom: Kasvatus- ja vähennysoperaatioiden käyttö lausekkeina tai lausekkeiden osina saattaa johtaa hyvin vaikeaselkoiseen ohjelmaan: Mitä esimerkiksi seuraava tulostaa:
int i=0; i = ++i + (++i - i++) + ++i + i++ + ++i; System.out.println(i);Tuollainen ohjelmointityyli on hyvin huonoa! Sitä ei sallita tällä kurssilla eikä sellaista arvosteta myöskään työelämässä!
Huom: Kasvatus- ja vähennysoperaatiota voi soveltaa vain muuttujiin ei arvoihin! Kaikki seuraavat ovat virheellisiä:
3++ ++4 ++(a+b) (c-d)++ ++i++ (++i)++ ++(i++)
= += -= *= /= %=Jos @ on jokin merkeistä +, -, *, /, %, niin
muuttuja @= lauseketarkoittaa
muuttuja = muuttuja @ lausekepaitsi, että muuttuja selvitetään vain kerran.
Muuttuja voi olla esim. indeksoitu taulukko, jonka indeksilausekkeessa
on sivuvaikutus. Vertaa seuraavia:
taulu[i++] += 7;
taulu[i++] = taulu[i++] + 7;
Joistakin ohjelmointikielistä poiketen Javassa arvon sijoittaminen muuttujaan on lauseke eli sillä itselläänkin on arvo (sijoitettavan lausekkeen arvo). On siis luvallista kirjoittaa:
int a=1,b=2,c=3,d=4; a += (b=c+d)+(d=7)-(c*=9);Mutta ei tuossa mitään järkeä ole! (En suosittele kokeessa kokeilemaan kepillä jäätä ;-)
Järkevää sitävastoin voisi olla vaikkapa yhteisen uuden alkuarvon antaminen:
int a=1,b=2,c=3,d=4; //... a = b = c = d = 77;Tällainen ketjutettu sijoitus lasketaan oikealta vasemmalle! So. d=77, sen arvo on 77, joka sijoitetaan c:lle, ...
> >= < <= == !=Kaikki vertailut tuottavat totuusarvon! On syytä pitää mielessa sijoitusoperaation = ja vertailuoperaation == ero. Molemmat kun voivat esiintyä lausekkeen osina ... Vahvan tyypityksen ansiosta Javalla ei tee virheitä tässä asiassa niin helposti kuin vaikkapa C:llä, mutta tarkkana on silti syytä olla!
Suuremmuutta tai pienemmyyttä tutkivilla operaatioilla voi vertailla vain numeerisia lausekkeita, yhtäsuuruus ja erisuuruus ovat käytettävissä monien muidenkin arvojen vertailussa.
&& ehdollinen "ja", "and" || ehdollinen "tai", "or" & "ja", "and" | "tai", "or" ^ poissulkeva "tai", "xor" (myös !=) ! negaatio, "not"Näiden operaatioiden operandit voivat olla vain totuusarvoisia lausekkeita: vertailuja tai totuusarvoisia lausekkeita. ("&", "|" ja "^" ovat myös bittijono-operaatioita.)
(totuusarvo ? lauseke1 : lauseke2)Totuusarvoinen lauseke lasketaan. Jos sen arvo on true, ehdollisen lausekkeen arvo on lauseke1, muuten lauseke2. (Sulkeet eivät ole välttämättömät, mutta suositeltavat.)
Joko lauseke1:n täytyy olla tyyppiä, joka ilman eksplisiittistä tyyppimuunnosta on sijoitettavissa lauseke2:n tyyppisen muuttujan arvoksi tai päinvastoin. Ehdollisen lausekkeen tyyppi on valittavien lausekkeiden tyypeistä laajempi.
Esimerkiksi ("a:lle ei-pienempi arvoista b ja c") ohjelmoidaan if-lauseella:
if (b < c) a = c; else a = b;ja ehdollisena lausekkeena:
a = (b < c ? c : b);On makuasia, kumpi tapa on selkeämpi, mutta ei ole makuasia, että valintaperusteena on oltava selkeys!
Pääsääntö laskentajärjestyksestä on, että samantasoiset (yhtä sitovat,
kts. jäljempänä) operaatiot lasketaan vasemmalta
oikealle:
Lausekkeessa x + y + z lasketaan ensin x:n arvo (mikä x sitten
onkaan, vaikkapa tulostavan metodin kutsu tai y:n arvoa muuttava
sijoitus!), sitten lasketaan y:n arvo. Seuraavaksi lasketaan x:n
ja y:n summa, sitten z ja lopuksi lasketaan lausekkeen jälkimmäinen
yhteenlasku. Esimerkiksi:
// Tästä esimerkistä ei pidä ottaa mallia!! int i=2, j=5; System.out.println( (j+=7) + ++i + (i+j) ); // x y z // 12 // 3 // 15 // 15 // 30 // tulostuu siis 30!
Kuten edellä jo todettiin, poikkeus laskentajärjestykseen on ketjutettu sijoitus: se lasketaan oikealta vasemmalle!
Javassa lausekkeen kaikki osat lasketaan, jopa silloin kun jossakin osassa sattuu vaikkapa nollalla jako. Ainoat poikkeukset ovat &&, || ja ? (ehdolliset and ja or sekä ehdollinen lauseke).
Tätä harjoiteltiin jo alakoulussa. Kun kysyttiin, mitä on
3 + 4 x 5
kiitosta sai vastauksesta "23", moitteita vastauksesta "35".
Näin opeteltiin aritmetiikan kielipeliä.
Operaatioiden sitovuus (tiukimmasta löysimpään) (luettelo on täydellinen; mukana on myös operaatioita joita kurssilla ei käsitellä):
[] . () e++ e-- indeksointi, komponenttiin viittaus, sulkulausekkeet, parametrit, kasvatus/vähennys jälkeenpäin ++e --e +e -e ! kasvatus/vähennys ennen, etumerkit, boolean not new (tyyppi)e olion luonti, ekspl. tyyppimuunnos * / % kertolaskut + - yhteenlaskut << >> >>> bittisiirrot < > >= <= instanceof vertailut == != yhtäsuuruusvertailut & and (bittijonolle) ^ xor (bittijonolle) | or (bittijonolle) && ehdollinen and || ehdollinen or ? ehdon soveltaminen = += -= *= /= %= sijoitusoperaatiot <<= >>= >>>= &= ^= |=Huom: Jos ei muista operaatioiden sitovuutta, sulkeilla voi itse määrätä laskentajärjestyksen! Usein niillä voi myös parantaa ohjelman luettavuutta ja selkeyttä.