Saturday, April 1, 2006

Java tutorial - Operatoren

Intro|Basics|Elementen|Bouwstenen|Datatypen|Operatoren|Toegangscontrole|Draaiboek|Foutenafhandeling|Objecten|Uitvoeren

Aanstuurders van de Java code

Voorrangsregels (precedences)

De voorrangsregels zijn belangrijk om te weten wanneer je met operatoren wil werken. De operatoren worden namelijk niet van links naar rechts uitgevoerd, zoals je de code schrijft. Er wordt eerst gekeken naar de aanwezige operatoren en dan worden de handelingen in de volgorde uitgevoerd zoals in de voorrangsregels is beschreven.

Hieronder staat een lijstje van alle in Java beschikbare operatoren in de volgorde van de prioriteit (de bovenste heeft voorrang boven de operatoren daaronder):

Postfix operatoren. . . . : [], ., (parameters), var++, var--
Prefix operatoren . . . . : ++var, --var, ~, !
Instantieren en casten. . : new Klasse, (SubKlasse)
Vermenigvuldigen en delen : *, /, %
Optellen en aftrekken . . : +, -
Bitjes verschuiven. . . . : <<, >>, >>>
Relationele operatoren. . : <, <=, >, >=, instanceof Klasse
Gelijkstellingen. . . . . : ==, !=
Booleanse/Binaire AND . . : &
Booleanse/Binaire XOR . . : ^
Booleanse/Binaire OR. . . : |
Conditionele AND. . . . . : &&
Conditionele OR . . . . . : ||
Conditionele if-else. . . : ? :
Toewijzingen. . . . . . . : =, +=, -=, *=, /=, %=, <<=, >>=, >>>=, &=, ^=, |=

parameters = invoer parameters (dit kunnen ook andere operatoren zijn)
var = de naam van een variabele
Klasse = de naam van een klasse
SubKlasse = de naam van een subklasse

Als je goed had opgelet tijdens de reken- of wiskundelessen, dan weet je ook wat de voorrangsregels inhouden. Laten we eens kijken naar het volgende eenvoudige rekensommetje:

2 + 3 * 4

De + en de - hebben een gelijkwaardige prioriteit (staan op een gelijke niveau in de lijst), echter de * heeft een hogere prioriteit (staat hoger in de lijst) en heeft dus voorrang. Het rekensommetje wordt dus als volgt uitgevoerd:

2 + (3 * 4)   =   2 + 12   =   14

Echter je kunt ook haakjes ( ) gebruiken om de voorrangsregels te beïnvloeden. Volgens het lijstje staan ze dan ook bovenaan in de lijst bij de postfix operatoren. De parameters die binnen de haakjes staan krijgen dus voorrang op de parameters buiten de haakjes. Voorbeeldje:

(2 + 3) * 4   =   5 * 4   =   20

De optelsom wordt nu dus eerst uitgevoerd in plaats van de vermenigvuldiging.

Terug naar boven

Toewijzings operator (assignment operator)

De toewijzings operator = heeft de volgende werking:

variabele = expressie

Wat zoveel betekent als dat variabele de uitkomst van expressie (het rekensommetje) ontvangt. Een expressie kan uit meerdere operanden bestaan. De eventuele vorige waarde van de variabele wordt altijd met de uitkomst van de expressie overgeschreven, onder de voorwaarde dat de variabele toegankelijk is, zie ook Toegangscontrole.

Terug naar boven

Rekenkundige operatoren (arithmetic operators)

De rekenkundige operatoren *, /, %, + en - kun je toepassen op alle nummerieke datatypen (dus geen booleans) en ze geven antwoord in vorm van een nummeriek datatype.

De hierboven genoemde operatoren kent iedereen vast wel, behalve misschien de modulus %. Deze geeft de restwaarde van een deling weer wanneer je als uitkomst een geheel getal wilt hebben. Bijvoorbeeld: 7 % 3 = 1. Even uitleggen: de deling 7 / 3 geeft geen geheel getal terug (2,333..). We nemen hier het gehele getal van (2) en vermenigvuldigen dat opnieuw met 3 wat dus een 6 oplevert. Het verschil tussen 6 en het oorspronkelijke getal 7 is 1. Dát is dus de uitkomst van 7 % 3. En op dezelfde manier geldt: 6 % 3 = 0 (er is geen restwaarde, de uitkomst is al een geheel getal).

int i;
i = 5 * 1;  // De waarde van i is nu 5.
i = 6 / 2;  // De waarde van i is nu 3.
i = 7 % 3;  // De waarde van i is nu 1.
i = 8 + 4;  // De waarde van i is nu 12.
i = 9 - 5;  // De waarde van i is nu 4.

Bij de String kun je de + gebruiken om meerdere strings aan elkaar te plakken.

// Declareer en initialiseer enkele strings.
String str1 = "test1";
String str2 = str1 + "test2";

// Laat ze zien:
System.out.println(str1);
System.out.println(str2);

test1
test1test2

Terug naar boven

Rekenkundige toewijzings operatoren (arithmetic assignment operators)

De rekenkundige toewijzings operatoren *=, /=, %=, += en -= verschillen van de rekenkundige operatoren dat ze de huidige waarde van de nummerieke datatype variabele gebruiken bij de expressie.

Dus bijvoorbeeld x += y is hetzelfde als x = x + y.

int i = 3;  // De waarde van i is 3.
i *= 6;     // De waarde van i is nu 3 * 6 = 18.
i /= 2;     // De waarde van i is nu 18 / 2 = 9.
i %= 4;     // De waarde van i is nu 9 % 4 = 1.
i += 5;     // De waarde van i is nu 1 + 5 = 6.
i -= 3;     // De waarde van i is nu 6 - 3 = 3.

Bij de String kun je de += gebruiken om een nieuwe string achter een bestaande string aan te plakken.

// Declareer en initialiseer een string.
String str = "test1";

// Plak er een nieuwe string achteraan.
str += "test2";

// Laat deze zien:
System.out.println(str);

test1test2

Terug naar boven

Vermeerdering en vermindering operatoren (increment and decrement operators)

De vermeerdering en vermindering operatoren ++ en -- zijn er in twee vormen: postfix (var++ en var--) en prefix (++var en --var). De postfix vermindering en vermeerdering operatoren kunnen de waarde van een datatype variabele één stap laten toenemen danwel afnemen, nádat de variabele wordt gebruikt bij een expressie.

int i = 0;
int j;

j = i++;

// De waarde van i is nu 1, maar j is nog 0 (de oude waarde van i).

De prefix vermeerdering en vermindering operatoren kunnen de waarde van een datatype variabele één stap laten toenemen danwel afnemen, vóórdat de variabele wordt gebruikt bij een expressie.

int i = 0;
int j;

j = ++i;

// De waarde van i én j zijn nu 1.
Terug naar boven

Booleanse operatoren (boolean operators)

De booleanse operatoren !, &, ^ en | kun je toepassen op booleanse waarden en ze geven ook weer booleanse antwoorden: true (waar) of false (onwaar).

!x (NOT) geeft de tegengestelde waarde terug.
x & y (AND) geeft true als de beide waarden true zijn.
x ^ y (XOR) geeft true als de beide waarden verschillend zijn.
x | y (OR) geeft true als ten minste één waarde true is.

x y !x !y x & y x ^ y x | y
true true false false true false true
true false false true false true true
false true true false false true true
false false true true false false false
Terug naar boven

Booleanse toewijzings operatoren (boolean assignment operators)

De booleanse toewijzings operatoren &=, ^= en |= verschillen van de booleanse operatoren dat ze de huidige waarde van de boolean variabele gebruiken bij de expressie.

Dus bijvoorbeeld x &= y is hetzelfde als x = x & y.

Terug naar boven

Relationele operatoren (relational operators)

De relationele operatoren <, <=, > en >= kun je toepassen op alle nummerieke datatypen (dus geen booleans) en ze kunnen alleen booleanse antwoorden geven.

x y x < y x <= y x > y x >= y
1 2 true true false false
2 2 false true false true
2 1 false false true true
Terug naar boven

Relationele gelijkstellings operatoren (relational equality operators)

De relationele gelijkstellings operatoren == en != worden gebruikt om twee operanden met elkaar te vergelijken. Je kunt deze toepassen op alle datatypen en objecten en ze geven booleanse antwoorden.

x == y (equals) geeft true als de beide waarden gelijk zijn, anders false.
x != y (not equal) geeft true als de beide waarden ongelijk zijn, anders false.

// Declareer en instantieer enkele datatypen.
int i1 = 10;
int i2 = 20;
int i3 = i2;

// Vergelijk de waarden.
boolean b1 = i1 == i2; // b1 = false, want verschillende waarde.
boolean b2 = i2 == i3; // b2 = true, want dezelfde waarde.
boolean b3 = i3 == 20; // b3 = true, want dezelfde waarde.

Je kunt deze ook toepassen op objecten, echter er wordt niet gekeken of ze van dezelfde klasse zijn, maar of ze van dezelfde instantie zijn.

// Declareer en instantieer enkele referentie-variabelen.
Auto auto1 = new Auto();
Auto auto2 = new Auto();
Auto auto3 = auto2;

// Vergelijk de instanties.
boolean b1 = auto1 == auto2;      // b1 = false, want verschillende instanties.
boolean b2 = auto2 == auto3;      // b2 = true, want dezelfde instanties.
boolean b3 = auto3 == new Auto(); // b3 = false, want verschillende instanties.

Let op: dit geldt dus ook voor strings! Ze zijn namelijk ook objecten. Je moet bij strings de methode equals() van de String klasse gebruiken om te kijken of de gegeven waarde overeenkomt met de huidige waarde van de string. Let wel op: wanneer je twee dezelfde String waarden binnen dezelfde scope (binnen eenzelfde methode) instantieert, dan verwijzen ze naar éénzelfde instantie in het cachegeheugen van de JVM.

// Declareer en instantieer een string.
String str1 = new String("test");
String str2 = "test";
String str3 = str2;

// Vergelijk de instanties.
boolean b1 = str1 == str2;        // b1 = false, want verschillende instanties.
boolean b2 = str2 == str3;        // b2 = true, want dezelfde instanties.
boolean b3 = str3 == "test";      // b3 = true, want dezelfde instantie in cachegeheugen.
boolean b4 = str1 == "test";      // b4 = false, want verschillende instanties.
boolean b5 = str1.equals(str2);   // b5 = true, want dezelfde waarde.
boolean b6 = str1.equals("test"); // b6 = true, want dezelfde waarde.
Terug naar boven

Conditionele operatoren (conditional operators)

De conditionele operatoren && en || hebben dezelfde werking als de booleanse operatoren & en |, met het verschil dat de conditionele operatoren al stoppen met de evaluatie wanneer de linker operand een false oplevert. De rechter operand wordt dan nooit getest. Simpel voorbeeldje:

// Testje met conditionele operatoren.
boolean b = (1 == 2) && (1 < 2);

De eerste operand (1 == 2) levert al een false op, de tweede operand (1 < 2) wordt nu nooit getest. Wanneer je de booleanse operator & zou gebruiken in plaats van de conditionele operator &&, dan worden de béide waarden gewoon getest.

De conditionele operatoren zijn nuttig in situaties, waarbij de tweede operand mogelijk een foutmelding zou kunnen geven, omdat de expressie van deze tweede operand niet uitgevoerd kan worden. In de eerste operand controleer je dan bijvoorbeeld of de gebruikte referentie-variabele niet null is. Voorbeeldje van zo'n foutmelding:

// Declareer een nieuwe en lege string.
String str = null;

// Kijk of de waarde overeenkomt met "test".
if (str.equals("test")) {
    System.out.println("Gelijk");
} else {
    System.out.println("Ongelijk");
}

Wellicht verwacht je hier de uitkomst Ongelijk, echter je krijgt een heel andere uitkomst: een NullPointerException foutmelding. De oorzaak is logisch: er is nog geen object toegewezen aan de referentie-variabele, de referentie-variabele is leeg en je kunt derhalve geen methoden aanroepen. Het toevoegen van een controle op de waarde null zou het probleem moeten verhelpen. En dit kan alleen wanneer je daar éérst op test met behulp van de conditionele operator &&.

// Declareer een nieuwe en lege string.
String str = null;

// Kijk of de waarde overeenkomt met "test".
if (str != null && str.equals("test")) {
    System.out.println("Gelijk");
} else {
    System.out.println("Ongelijk");
}

En de uitkomst is nu wel Ongelijk. Met de booleanse operator & die per-se de beide operanden wil testen zou je nog steeds een NullPointerException krijgen.

Overigens kun je dit soort potentiële NullPointerException fouten ook op de volgende manier afvangen:

// Declareer een nieuwe en lege string.
String str = null;

// Kijk of "test" overeenkomt met de waarde.
if ("test".equals(str)) {
    System.out.println("Gelijk");
} else {
    System.out.println("Ongelijk");
}

De String waarde "test" is namelijk niet gelijk aan null.

Terug naar boven

Conditionele if-else operator (conditional if-else operator)

De conditionele if-else operator ? : is een soort afkorting van een if-else statement. Je zou het zelfs in plaats daarvan kunnen gebruiken, maar dit is in verband met de leesbaarheid af te raden. Het wordt aangeraden om de conditionele if-else operator alleen binnen een expressie te gebruiken. Het werkt als volgt:

variabele = voorwaarde ? expressie1 : expressie2

Wanneer voorwaarde een true oplevert, dan wordt expressie1 uitgevoerd en toegewezen aan variabele, anders wordt expressie2 uitgevoerd en toegewezen aan variabele. Voorbeeldje:

// Even een integer declareren.
int i;

// De 'normale' if-else statement.
if (1 == 2) {
    i = 0;
} else {
    i = 1;
}

// Nu met de conditionele if-else operator die precies zo werkt.
i = (1 == 2) ? 0 : 1;

// Zo werkt het trouwens ook, maar dat is nog slechter leesbaar.
i = 1 == 2 ? 0 : 1;
Terug naar boven

Klasse operatoren (class operators)

De klasse operatoren new Klasse, instanceof Klasse, (SubKlasse) en . (punt) kunnen alleen worden toegepast op objecten. Met new Klasse kun je een een object instantieren. Met instanceof Klasse kun je testen of een referentie-variabele een instantie van de gegeven klasse bevat, dit geeft een booleanse antwoord. Voorbeeldje:

// Instantieer de referentie-variabele "alfaRomeo" als een nieuwe Auto.
Auto alfaRomeo = new Auto();

// Kijk of alfaRomeo inderdaad een instantie is van de klasse Auto.
if (alfaRomeo instanceof Auto) {
    System.out.println("alfaRomeo is een instantie van de klasse Auto");
} else {
    System.out.println("alfaRomeo is geen instantie van de klasse Auto");
}

Met de cast-operator (SubKlasse) kun je een referentie-variabele converteren van een superklasse naar een subklasse. Dit wordt nader toegelicht in Objecten - Objecten converteren.

De . (punt) wordt gebruikt om de methoden van een ander (reeds geinstantieerd) object te aanroepen of om daarvan de variabelen te benaderen. Zie ook Elementen - Aanroepen.

Terug naar boven

Array operator (array operator)

De array operator [] geeft aan dat de variabele een array is. Een array is een groep losse waarden binnen één variabele. In veel gevallen worden array's gebruikt wanneer deze waarden een samenhang met elkaar hebben. Bijvoorbeeld de hoeveelheid aangemaakte auto's per maand, gerangschikt naar de maand. Tussen de blokhaken van de array operator kun je een index opgeven. Bij het initialiseren moet je de grootte van de array als een index opgeven. Wanneer je een array variabele afzonderlijk wilt initialiseren, dan kun je de positie binnen de array als een index opgeven. Dit begint altijd met een nul.

// Declareer en initialiseer een integer variabele als een array van 3 waarden.
int[] array1 = new int[3];

// Initialiseer de array met de gewenste waarden.
array1[0] = 10;
array1[1] = 18;
array1[2] = 15;

// Je kan de array's ook tegelijk declareren en initialiseren.
int[] array2 = {10, 18, 15};
String[] array3 = {"foo", "bar", "meep"};

Dit kun je gezien de String array dus ook bij objecten toepassen, echter het is aanbevelenswaardig om de array operator alleen voor primitieve datatypen te gebruiken. Wanneer je een array van objecten wil maken, dan ben je veel beter af met een Collection of een Map. Deze zijn voorzien van veel handige functies. Zie ook Collecties en Mappen.

Terug naar boven

Binaire operatoren (binary/bitwise operators)

De binaire operatoren ~, &, ^, |, &=, ^=, |=, <<, >>, >>>, <<=, >>= en >>>= zijn echt een vak apart. Ze rekenen niet met nummerieke waarden, maar met de binaire waarden daarvan (allemaal nullen en enen). Ze worden hier niet in detail toegelicht, maar de betekenis van deze operatoren wordt wel even samengevat met een paar voorbeeldjes. Let wel, er komen inderdaad booleanse operatoren in voor, maar dit wordt wel door de Java compiler gecontroleerd aan de hand van de datatype van de variabelen die worden gebruikt bij de expressie.

operator betekenis
~ De binaire equivalent van de booleanse NOT operator !
Voorbeeldjes: ~0 = 1, ~1 = 0
& De binaire equivalent van de booleanse AND operator &
Voorbeeldjes: 0 & 0 = 0, 0 & 1 = 0, 1 & 0 = 0, 1 & 1 = 1
^ De binaire equivalent van de booleanse XOR operator ^
Voorbeeldjes: 0 ^ 0 = 0, 0 ^ 1 = 1, 1 ^ 0 = 1, 1 ^ 1 = 0
| De binaire equivalent van de booleanse OR operator |
Voorbeeldjes: 0 & 0 = 0, 0 & 1 = 0, 1 & 1 = 1
&= De binaire equivalent van de toewijzende booleanse AND operator &=
Uitleg: x &= y is hetzelfde als x = x & y
^= De binaire equivalent van de toewijzende booleanse XOR operator ^=
Uitleg: x ^= y is hetzelfde als x = x ^ y
|= De binaire equivalent van de toewijzende booleanse OR operator |=
Uitleg: x |= y is hetzelfde als x = x | y
<< De binaire shift left operator.
Uitleg: bij x << y worden er y-aantal nullen aan het eind toegevoegd en worden de eerste y bits van het begin verwijderd.
>> De binaire shift right operator.
Uitleg: bij x >> y worden er y-aantal bits aan het begin toegevoegd en worden de laatste y bits van het eind verwijderd. De toegevoegde bits zijn nullen bij positieve integers en enen bij negatieve integers.
>>> De binaire shift right with zero fill operator.
Uitleg: bij x >>> y worden er y-aantal nullen aan het begin toegevoegd en worden de laatste y bits van het eind verwijderd. Er worden dus gewoon nullen toegevoegd, ongeacht of de integer positief of negatief is.
<<= De toewijzende binaire shift left operator.
Uitleg: x <<= y is hetzelfde als x = x << y
>>= De toewijzende binaire shift right operator.
Uitleg: x >>= y is hetzelfde als x = x >> y
>>>= De toewijzende binaire shift right with zero fill operator.
Uitleg: x >>>= y is hetzelfde als x = x >>> y

Ze worden in regel toegepast om snel in binair niveau te kunnen rekenen, bijvoorbeeld in stuurprogramma's om microcontrollers direct te aansturen.

Een goed voorbeeld van een wat algemenere toepassing van de binaire operatoren is het spelen met RGB kleuren. Op de volgende manier kun je met behulp van de binaire operatoren snel de juiste kleuren uit een hexadecimale RGB string halen:

// Declareer en initialiseer een RGB kleur in een String met een hexadecimale waarde.
String color = "ff3399";

// Converteer deze naar een int.
int rgb = Integer.parseInt(color, 16); // rgb is nu hetzelfde als 0xff3399.

// Vraag de laatste byte (de "b") op.
int blauw = rgb & 0xff;

// Schuif 8 bits naar rechts ("b" wordt verwijderd, je krijgt nu "rg")
rgb >>= 8;

// Vraag de laatste byte (de "g") op.
int groen = rgb & 0xff;

// Schuif 8 bits naar rechts ("g" wordt verwijderd, je krijgt nu "r")
rgb >>= 8;

// Vraag de laatste byte (de "r") op.
int rood = rgb & 0xff;

// Laat ze zien.
System.out.println(Integer.toHexString(rood));
System.out.println(Integer.toHexString(groen));
System.out.println(Integer.toHexString(blauw));

ff
33
99

Shiften is daarnaast ook fractioneel sneller dan vermenigvuldigen en delen. Hieronder staan enkele praktijkvoorbeeldjes:

// Declareer en initialiseer een willekeurige integer.
int i = 16;

// Dit is hetzelfde als i * 2.
int j = i << 1;

// Dit is hetzelfde als i * 4.
int k = i << 2;

// Dit is hetzelfde als i * 8.
int l = i << 3;

// Dit is hetzelfde als i / 2.
int m = i >> 1;

// Dit is hetzelfde als i / 4.
int n = i >> 2;

// Dit is hetzelfde als i / 8.
int o = i >> 3;
Terug naar boven


In het volgende hoofdstuk wordt toegelicht hoe je de zichtbaarheid van de klassen, variabelen en methoden kunt beïnvloeden: Toegangscontrole.

Copyright - Niets van deze pagina mag worden overgenomen zonder uitdrukkelijke toestemming.

(C) April 2006, BalusC

5 comments:

Ruud said...

Volgens mij staat er in een fout in het voorbeeld bij de Rekenkundige toewijzings operatoren (arithmetic assignment operators).
De regel i %= 4; zou op dat moment overeenkomen met 9 % 4 en de uitkomst hiervan is 1 en niet 5.

BalusC said...

Oeps! Fixed! Thanks!

David said...

Heel handig om JAVA mee te leren, bedankt voor alle moeite om deze goede tut. te maken

Rashnu Philips said...

Nog een foutje :
| De binaire equivalent van de booleanse OR operator |
Voorbeeldjes: 0 & 0 = 0, 0 & 1 = 0, 1 & 1 = 1

bevat & ipv | in de voorbeelden

Gerard Bouwhuis said...

Very usefull, thank's for making this.