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ä, ...?
Sääntöjä:
Esimerkki (nimetään muuttujat vähän erikoisesti alkamaan merkillä "_"):
short _short = -23; int _int = 123; long _long = 121234; char _char = 'q'; float _float = 3.14f; double _double = 121.2311; // sallittuja kiellettyjä _int = _short; _short = _int; _long = _int; _int = _long; _double = _short; _short = _double; _double = _float; _float = _double; _int = _char; _char = _int; _short = _char; _char = _short; _double = _char; _short = 8-0; // kokonaislukulaskutoimitukset tehdään // aina vähintään int-tyyppisinä!!Kääntäjä antaa seuraavantapaisia ilmoituksia (4. virheellinen tapaus):
Ohjelma.java:12: possible loss of precision found : double required: float _float = _double; ^ 1 error
Eksplisiittinen tyyppimuunnos (cast) ilmaistaan kirjoittamalla arvon saajan tyyppinimi sulkeisiin lausekkeen eteen:
short _short = -23; int _int = 123; long _long = 121234; char _char = 'q'; float _float = 3.14f; double _double = 121.2311; _short = (short)_int; _int = (int)_long; _short = (short)_double; _float = (float)_double; _char = (char)_int; _short = (short)_char; _char = (char)_short; _short = (short)(8-0); _short = (short)(3.14 * 1000.3 - 0.00002); // huom. sulkeet lausekkeen ympärillä!!
Järkevillä tapauksilla on järkevä tulkinta: liukuluku katkaistaan kokonaisluvuksi. Kun double-arvosta tehdään float, esitystarkkuus voi pienetä; sopivankokoinen kokonaisluku voidaan muuttaa char-arvoksi, ... (Tarkemmin asiaa esitellään spesifikaatiossa: 5.1.3 Narrowing Primitive Conversions)
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.17.3 Remainder Operator %.)
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.718281828459045) Math.PI // luku pii (3.141592653589793)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.minMonet Math-luokan metodit on kuormitettu eri tyyppisille parametreille. API-kuvauksessa on täydellinen luettelo Math-luokan metodeista.
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++)
Tavallisen sijoituksen lisäksi käytössä on joukko muuttujan vanhaa arvoa muuttavia operaatioita, mm. seuraavat:
= += -= *= /= %=Esimerkiksi
muuttuja *= lauseketarkoittaa
muuttuja = muuttuja * lausekepaitsi, että muuttuja selvitetään vain kerran.
Asialla on merkitystä, jos muuttujan selvittämisellä on
sivuvaikutuksia.
Esimerkki tällaisesta tilanteesta:
Muuttuja on indeksoitu taulukko, jonka indeksilausekkeessa
on sivuvaikutus. Vertaa seuraavia lauseita:
taulu[i++] += 7; taulu[i++] = taulu[i++] + 7;Tällainen ohjelmointityyli on hyvin epäselvää ja ja siksi virhealtista!
Monista ohjelmointikielistä poiketen Javassa arvon sijoittaminen muuttujaan on siis 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ä kaikkien 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ä.