Saturday, April 1, 2006

Java tutorial (all Dutch)

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

De weg naar Java

Introductie

In deze Nederlandstalige Java tutorial wordt uitgelegd hoe het object georiënteerde programmeertaal Java in elkaar steekt. Java is een krachtige platform-onafhankelijke taal dat op praktisch alle gebieden toegepast kan worden. Van mobiele telefoons (Java Micro Edition, Java ME), via computerprogramma's (Java Standard Edition, Java SE) tot grote dynamische webapplicaties (Java Enterprise Edition, Java EE).

Er is bewust gekozen voor het Nederlands als voertaal van deze tutorial, omdat praktisch alle Java tutorials Engelstalig zijn, dat het nogal kansloos zou zijn om daar nog eentje achteraan te gooien. Alle in de tutorial gebruikte termen zijn dan ook wel Nederlandstalig (hoewel hier en daar wellicht wat krom ;) ), maar er worden in de relevante gevallen altijd tussen haakjes de gerelateerde Engelstalige term meegegeven.

Terug naar boven

Kenmerken

Eenvoudig: de taal steekt (achteraf gezien natuurlijk) vrij eenvoudig in elkaar.

Veelzijdig: het kan praktisch overal voor gebruikt worden, van eenvoudige spelletjes voor mobiele telefoons tot krachtige en veilige websites voor het Internetbankieren.

Platform-onafhankelijk: eenzelfde Java applicatie kan op praktisch álle machines uitgevoerd worden, ongeacht het gebruikte hardware en besturingssysteem.

Herbruikbaar: een goede Java applicatie bestaat uit meerdere onderdelen (klassen en methoden) die op zich relatief makkelijk herbruikbaar zijn.

Multithreaded: de onderdelen (klassen en methoden) van een Java applicatie kunnen tegelijkertijd meerdere keren worden aangeroepen en uitgevoerd, in plaats van 1x tegelijk en dat slechts achter elkaar (singlethreaded). Dit is bevorderlijk voor de performance.

Terug naar boven

Werking

Een Java applicatie (.java) moet eerst tot bytecode (.class) worden gecompileerd, voordat het uitgevoerd kan worden. Deze bytecode kan dan worden uitgevoerd door een Java Virtual Machine (JVM). Een JVM kan ook de bytecode opnieuw compileren naar machinecode (Just In Time compilatie, JIT), waardoor het veel sneller uitgevoerd kan worden. Er zijn diverse JVM's beschikbaar voor verschillende platformen. Hierdoor kan precies eenzelfde Java applicatie uitgevoerd worden op praktisch álle machines en besturingssystemen. Een ander voordeel is dat de JVM danwel de JIT compiler geoptimaliseerd kan worden voor specifieke software danwel hardware (Windows, Linux, Solaris, Intel processor, AMD processor, IBM processor, etc).

Terug naar boven

Software

Voordat je Java applicaties op een computer kunt uitvoeren, heb je dus een JVM voor de computer nodig. Je kunt deze downloaden op de website van Sun: Java Standard Edition. Je kunt er o.a. de JRE en de JDK downloaden. De JRE (Java Runtime Environment) is goed genoeg als je alleen maar Java applicaties wilt kunnen uitvoeren. De JDK (Java Development Kit) is nuttig als je ook wilt programmeren in Java. De JDK wordt in geval van Java EE trouwens ook weleens SDK genoemd (Software Development Kit), omdat je daar een complete ontwikkelomgeving en een applicatieserver bij krijgt.

Aangezien je deze tutorial zit te lezen, gaan we ervan uit dat je wil leren om in Java te gaan programmeren, dan kun je het beste de JDK (zonder de NetBeans) downloaden en installeren. Je zou daarna Java kunnen gaan programmeren in een teksteditor, in de commandconsole compileren met javac.exe en uitvoeren met java.exe. Goede teksteditors met highlighters (de bouwstenen van de code worden in kleurtjes weergeven ter bevordering van de leesbaarheid) zijn EditPlus, Notepad++ en UltraEdit. BalusC gebruikt zelf EditPlus, maar Notepad++ is freeware en ook prima om mee te beginnen.

Er zijn echter ook IDE's (Integrated Development Environment) voor Java beschikbaar. Een IDE is een geavanceerde ontwikkelomgeving met een heleboel ingebouwde functies specifiek op de code toegespitst. Met vele shortcuts, code-generators, real-time compilen/uitvoeren, code-formatters, step-by-step debuggers, etcetera, is het ideaal voor de veeleisende Java developer. Veel IDE's zijn kostbaar, zoals Websphere Studio Application Developer, een IDE voor Java EE, waar BalusC op zijn werk veel mee werkt. Echter er zijn ook gratis open-source IDE's voor Java beschikbaar, zoals Eclipse, JBoss, Netbeans IDE, Sun Java Studio Creator. Jboss en Websphere Studio zijn trouwens gebaseerd op de engine van Eclipse.

Terug naar boven

Hello World in Eclipse

Een Hello World applicatie is een stukje code die je bij bij wijze van een eerste oefening kunt schrijven om te kijken hoe de basiscode in elkaar zit en of het werkt. Hieronder staat een voorbeeld van een Hello World Java applicatie:

package test;

// De HelloWorld klasse.
public class HelloWorld {

    // De main() methode.
    public static void main(String[] args) {

        // Schrijf "Hello World" naar de systeem console.
        System.out.println("Hello World");
    }

}

We gaan nu even Eclipse installeren om de Hello World te uitvoeren. Van alle freeware Java IDE's is Eclipse veruit de beste keuze, het is enorm uitgebreid en bij de Java EE versie kun je dankzij de ingebouwde WTP (Web Tools Platform) ook direct Java EE webapplicaties mee ontwikkelen.

Eclipse installeren

  1. Mocht je dat niet al gedaan te hebben, download en installeer de laatste Java SE JDK (let op: de JDK en dus niet de JRE). Neem die ene versie zonder NetBeans en Java EE. Deze tutorial is overigens geschreven ten tijde van Java SE JDK 5.0.
  2. Ga naar de download pagina van Eclipse.
  3. Klik op het linkje achter "Eclipse IDE for Java EE Developers".
  4. Klik daarna op het linkje achter "Download from:" of kies een andere mirror.
  5. Pak de zip uit, je zult een mapje tegenkomen genaamd "eclipse".
  6. Kopieer/verplaats deze map naar jouw Program Files directory (niet noodzakelijk, maar wel handig).
  7. Voer de eclipse.exe uit.
  8. Geef een workspace op (hier worden jouw Java projecten bewaard). Suggestie: \Documenten\Java\Eclipse.
  9. Klik op de welkomstscherm rechtsboven op de gebogen pijl: Go to the workbench.

Hello World Java Applicatie bouwen

  1. Maak een Java Project aan: File - New - Project... - Java Project en klik op Next.
  2. Geef als projectnaam "HelloWorld" op, laat de rest van de velden standaard en klik op Finish. Je zult hierna de vraag krijgen: "Open Associated Perspective?". Klik op Yes.
  3. Rechtsklik op de zojuist aangemaakte project: New - Class.
  4. Kies een package naam, bijvoorbeeld "test" en geef de klasse de naam "HelloWorld". Laat de rest van de velden standaard en klik op Finish.
  5. Neem de Hello World voorbeeldcode over in de zojuist aangemaakte HelloWorld.java klasse en sla deze op.
  6. Voer de Java applicatie uit: Run - Run As - Java Application.
  7. In de systeem console zul je de tekst "Hello World" zien! Zie ook de Screenshot:
    Hello World
Terug naar boven

Eclipse verkennen

Deze IDE kun je gedurende deze tutorial wel blijven gebruiken om even mee te spelen. Theorie is allemaal heel leuk, maar je leert er pas van wanneer je het ook daadwerkelijk in de praktijk toepast. Dus kopieer/plak zoveel mogelijk toepasbare code om deze in de IDE te laten uitvoeren of, veel liever, typ deze zelf over. Je moet het uiteindelijk toch zélf in (uit) je vingers krijgen ;)

Eclipse kent enorm veel handige functies en shortcuts. Ga gerust op verkenning uit via de contextmenu's. Hier worden nog wel enkele "must to know" dingetjes vermeld:

  1. Via Window - Preferences kun je extreem veel opties instellen. Die in de groep "Java" zijn het interessantst. Bijvoorbeeld de Compiler Errors/Warnings.
    Compiler Errors/Warnings
  2. CTRL+spatie in de code levert een dropdown box op waarin alle beschikbare klassen en/of methoden te selecteren zijn. Zodra er maar 1 mogelijkheid is, dan wordt er gewoon automatisch aangevuld. Dit geldt ook voor de zogenaamde "code templates", probeer maar bijvoorbeeld main[CTRL+spatie] binnen een klasse en sysout[CTRL+spatie] binnen een methode. Deze zijn instelbaar via Window - Preferences - Java - Editor - Templates.
    CTRL+Spatie
  3. CTRL+1 in de regel waar een compileerfout (rood onderstreept) of een waarschuwing (geel onderstreept) staat, levert een dropdown box op met alle mogelijke oplossingen. Dezelfde dropdown kun je krijgen wanneer je op het bolletje links naast de regel klikt.
    CTRL+1
  4. CTRL+SHIFT+F formatteert de code netjes volgens de richtlijnen die je via Window - Preferences - Java - Code Style - Formatter kunt instellen.
    CTRL+SHIFT+F
  5. CTRL+SHIFT+O organiseert de imports volgens de richtlijnen die je via Window - Preferences - Java - Code Style - Organize Imports kunt instellen.
    CTRL+SHIFT+O
  6. CTRL+SHIFT+G op een gedeclareerde klasse, methode of variabele zoekt naar referenties van de klasse, de methode danwel de variabele.
  7. ALT+SHIFT+R op een gedeclareerde klasse, methode of variabele biedt de mogelijkheid om deze dynamisch te hernoemen. Hierbij worden dus ook alle referenties hernoemd.
    ALT+SHIFT+R
  8. CTRL+muisklik op een gedeclareerde klasse, methode of variabele gaat direct naar de broncode daarvan. Een kale Eclipse installatie kent de broncode van de JDK nog niet, waardoor je een "Source not found" foutmelding zult krijgen wanneer je een CTRL+muisklik doet op de standaard API van Java, bijvoorbeeld String. Je zult dan via Attach source - External file het bestand src.zip moeten aangeven. Dit hoef je slechts eenmalig te doen. Dit bestand staat normaliter in de installatiedirectory van de JDK. De onderstaande screenshot toont het resultaat van een CTRL+muisklik op het println gedeelte van System.out.println.
    CTRL+klik

Mocht je op een of andere manier de hele Eclipse omgeving (instellingen, voorkeuren, gedragingen, workspace, etcetera) te hebben verkracht, waardoor je naar de vers geinstalleerde staat terugverlangt, verwijder dan de folder genaamd ".metadata" uit de workspace. In eerdergenoemd voorbeeld is dat dus \Documenten\Java\Eclipse\.metadata.

Terug naar boven


In het volgende hoofdstuk wordt het Object Oriented Programming nader uitgelegd: Basics.

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

(C) April 2006, BalusC

Java tutorial - Basics

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

Object Oriented Programming basics

Abstracties (abstractions)

Om in Java of ieder ander willekeurig object georiënteerde programeertaal te kunnen programmeren, moet je een vermogen hebben om abstract (conceptueel) te kunnen denken. Je moet kunnen kijken naar de essentiële eigenschappen en gedragingen van diverse objecten (voorwerpen). Je moet kunnen kijken naar de overeenkomsten en deze per object te kunnen vastleggen.

Bijvoorbeeld: Alle auto's hebben de volgende overeenkomsten: ze hebben een motor, ze verbruiken brandstof, ze kunnen rijden, ze zijn voorzien van verlichting, ze hebben een aantal zitplaatsen, etc. Alle auto's kunnen tot eenzelfde concept (de klasse) gerekend worden.

Terug naar boven

Klassen (classes)

Een klasse modelleert een abstractie (een concept) door de eigenschappen en de gedragingen te vastleggen.

Bijvoorbeeld: Alle auto's hebben de volgende eigenschappen: een cylinderinhoud, een gewicht, een toerental, een brandstofsoort, een versnelling, een snelheid, een verbruik, een aantal zitplaatsen, etc. Alle auto's hebben de volgende gedragingen: instappen/uitstappen, starten/stoppen, gasgeven/schakelen/remmen, etc.

Terug naar boven

Objecten (objects)

Een object moet alle in de klasse vastgelegde eigenschappen en gedragingen kunnen vertonen. De eigenschappen zijn hetzelfde bij alle objecten van eenzelfde klasse, maar de waarden kunnen per object danwel situatie verschillen. De gedragingen zijn afhankelijk van de eigenschappen en de resultaten van deze gedragingen kunnen per object verschillen.

Bijvoorbeeld: Alle auto's (alle objecten van dezelfde klasse) hebben de volgende eigenschappen die per auto (een object) kunnen verschillen: de cylinderinhoud, het gewicht, het minimum en maximum toerental, het maximum aantal zitplaatsen, de brandstofsoort, etc. Een auto heeft de volgende eigenschappen die per situatie kunnen verschillen: het toerental, de versnelling, het aantal bezette zitplaatsen, de snelheid, het verbruik, etc.

Terug naar boven

Variabelen (variables)

Een variabele legt een eigenschap vast met een waarde dat per object danwel situatie kan verschillen.

Bijvoorbeeld: Een bepaalde auto (een object) heeft een cylinderinhoud van 1910cc, een gewicht van 1330kg, een minimum toerental van 850rpm, een maximum toerental van 4500rpm, een maximum aantal versnellingen van 5, een maximum aantal zitplaatsen van 5, diesel als brandstofsoort, etc. Alle auto's (alle objecten van dezelfde klasse) hebben naar gelang de situatie een wisselend toerental, een wisselende versnelling, een wisselend aantal bezette zitplaatsen, een wisselend snelheid, een wisselend verbruik, etc.

Let op: Een variabele kan óók een object bevatten. Dan heb je dus een hele auto (een object) in één variabele. We spreken hier dan over een instantie. Dit wordt later in deze tutorial beschreven.

Terug naar boven

Methoden (methods)

Een methode beschrijft hoe de gedragingen uitgevoerd moeten worden op basis van de opgegeven informatie (de invoer). De methoden passen de eigenschappen (de variabelen) aan naar gelang de invoer en de huidige situatie. Dit levert een nieuwe situatie op.

Bijvoorbeeld: Het gasgeven (een methode) laat het toerental (een eigenschap) in waarde toenemen. Het instappen laat het aantal beschikbare zitplaatsen in waarde afnemen. De snelheid is afhankelijk van het toerental, de ingestelde versnelling en het gewicht. Het verbruik is afhankelijk van de gemiddelde snelheid, het gemiddeld toerental, de cylinderinhoud en de brandstofsoort.

Terug naar boven

Voorbeeld code

De Java code van het concept "Auto" zoals beschreven in de bovenstaande voorbeelden zou er als volgt kunnen eruitzien:

package nl.balusc.voertuigen;

// De klasse "Auto".
public class Auto {

    // De eigenschappen die voor de hele klasse gelden.
    public static int aantalAutos;

    // De eigenschappen die per auto kunnen verschillen.
    public String autoMerkType = "";
    public int cylinderinhoud;
    public int gewicht;
    public int minimumToerental;
    public int maximumToerental;
    public int maximumVersnelling;
    public int maximumZitplaatsen;
    public String brandstofSoort;

    // De eigenschappen die voor elke auto per situatie kunnen verschillen.
    private int toerental;
    private int versnelling;
    private int zitplaatsenBezet;
    private double snelheid;
    private double verbruik;
    private boolean verlichtingAan;

    // De zogenaamde constructeur. Dit is een speciale methode dat hetzelfde naam
    // als de klasse heeft. Dit wordt bij elke auto aangeroepen en in dit geval
    // wordt er een auto aangemaakt en de hoeveelheid auto's met 1 toegenomen.
    public Auto() {
        maakAuto();
        aantalAutos++;
    }

    // Je kunt meerdere constructeurs in een klasse hebben, elk met een specifieke
    // invoer. In deze constructeur kun je dus een specifiek merk en type opgeven.
    public Auto(String merkType) {
        autoMerkType = merkType;
        maakAuto();
        aantalAutos++;
    }

    // De eigenschappen worden ingesteld, afhankelijk van het merk en de type.
    public void maakAuto() {
        if (autoMerkType.equals("Alfa Romeo 156 1.9 JTD SW")) {
            cylinderinhoud = 1910;
            gewicht = 1330;
            minimumToerental = 850;
            maximumToerental = 4500;
            maximumVersnelling = 5;
            maximumZitplaatsen = 5;
            brandstofSoort = "Diesel";
        } else if (autoMerkType.equals("BMW M6")) {
            cylinderinhoud = 4999;
            gewicht = 1710;
            minimumToerental = 750;
            maximumToerental = 7750;
            maximumVersnelling = 6;
            maximumZitplaatsen = 4;
            brandstofSoort = "Benzine";
        } else { // Onbekende auto, dus we maken een "standaard" auto.
            cylinderinhoud = 2000;
            gewicht = 1250;
            minimumToerental = 1000;
            maximumToerental = 6000;
            maximumVersnelling = 5;
            maximumZitplaatsen = 5;
            brandstofSoort = "Benzine";
        }
    }

    // De methode "instappen".
    public void instappen(int aantalMensen) {
        zitplaatsenBezet = zitplaatsenBezet + aantalMensen;
        
        if (zitplaatsenBezet > maximumZitplaatsen) {
            int teveelMensen = zitplaatsenBezet - maximumZitplaatsen;
            System.out.println("Er zijn " + maximumZitplaatsen + " zitplaatsen.");
            System.out.println("Er moeten " + teveelMensen + " mensen eruit.");
        }
    }

    // De methode "uitstappen".
    public void uitstappen(int aantalMensen) {
        zitplaatsenBezet = zitplaatsenBezet - aantalMensen;
    }

    // De methode "starten".
    public void starten() {
        toerental = minimumToerental;
        verlichtingAan = true;
    }

    // De methode "stoppen".
    public void stoppen() {
        toerental = 0;
        verlichtingAan = false;
    }

    // De methode "gasgeven".
    public void gasgeven(int aantalToeren) {
        toerental = toerental + aantalToeren;
        
        if (toerental > maximumToerental) {
            toerental = maximumToerental;
            System.out.println("Niet teveel gasgeven he!");
            System.out.println("De toerentalbegrenzer is in werking getreden.");
        }
    }

    // De methode "schakelen".
    public void schakelen(int aantalZetten) {
        versnelling = versnelling + aantalZetten;
        
        if (versnelling > maximumVersnelling) {
            versnelling = maximumVersnelling;
            System.out.println("Er is geen hogere versnelling beschikbaar.");
        }
        // Hier moet ook berekend kunnen worden dat het toerental met een bepaald
        // aantal toeren bijgesteld moet worden.
    }

    // De methode "remmen".
    public void remmen(int remkracht) {
        // Bereken hier het nieuwe toerental en de nieuwe vernsnelling aan de hand
        // van de remkracht.
    }

    // De methode om het huidige verbruik te bereken en verkrijgen.
    public double berekenVerbruik() {
        // Bereken hier het verbruik aan de hand van het gemiddelde snelheid, het
        // gemiddeld toerental, de cylinderinhoud en de brandstofsoort.
        return verbruik;
    }

    // De methode om de huidige snelheid te bereken en verkrijgen.
    public double berekenSnelheid() {
        // Bereken hier de snelheid aan de hand van het toerental, de ingestelde
        // versnelling en het gewicht.
        return snelheid;
    }

    // De methode om het huidige aantal vrije zitplaatsen te bereken en verkrijgen.
    public int berekenAantalZitplaatsenVrij() {
        int aantalZitplaatsenVrij = maximumZitplaatsen - zitplaatsenBezet;
        return aantalZitplaatsenVrij;
    }

}
Terug naar boven

Dat was een flinke lap ..

En dat was in theorie ook nog verre van compleet. Code-technisch gezien kan dit ook wel netter (de invoer controleren, de eventuele excepties afvangen, de eigenschappen van verschillende auto's opsommen, etc), maar dan wordt het voor de beginner veel te ingewikkeld. Op het moment zou slechts het lezen van de commentaren, de benamingen van de eigenschappen (de variabelen) en die van de gedragingen (de methoden) met behulp van enig wiskundig/logisch inzicht afdoende zijn om de insteek van deze code te begrijpen. De afzonderlijke onderdelen en verfijningen worden gedurende deze tutorial wel tot in detail toegelicht.

Terug naar boven


In het volgende hoofdstuk wordt globaal uitgelegd hoe je met de klassen, objecten, variabelen en methoden kunt werken: Elementen.

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

(C) April 2006, BalusC

Java tutorial - Elementen

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

Werken met de Java code

Declareren (declarations)

Voordat je met de elementen (klassen, variabelen, methoden en objecten) kan werken, moet je ze eerst declareren (aangeven, vaststellen). De constructie van een declaratie is als volgt:

<toegangsmodifier(s)> <element type> <benaming>

De toegangsmodifier(s) zijn zogezegd de verkeersregelaars van de Java code. Deze zijn optioneel, het kan leeg blijven, het kan er maar één zijn of er zijn er meerdere. Deze worden uitgebreid beschreven in het hoofdstuk Toegangscontrole.

De element type is verplicht. De klasse heeft altijd de element type class. Bij de variabelen geeft het aan wat voor een datatype of object de variabele is. Bij de methoden geeft het aan wat voor een antwoord de methode geeft. De element type moet dan overeenkomen met de inhoud van de return statement van de methode. Er kunnen methoden zijn die helemaal niet antwoorden, dan moet je als element type void invullen.

Tot slot de benaming; deze spreekt wel voor zich. De voorwaarden voor een goede benaming kun je in het hoofdstuk Bouwstenen - Benamingen lezen.

In het onderstaand voorbeeld worden de klasse, twee variabelen en twee methoden gedeclareerd.

// De klasse "Auto" wordt gedeclareerd.
public class Auto {

    // De twee variabelen "toerental" en "verlichtingAan" worden gedeclareerd.
    // De eerste kan alleen een "int" bevatten en de tweede een "boolean".
    private int toerental;
    private boolean verlichtingAan;

    // De methode "starten" wordt gedeclareerd. Deze methode geeft
    // geen antwoord en wordt derhalve als "void" gedeclareerd.
    public void starten() {
        toerental = minimumToerental;
        verlichtingAan = true;
    }

    // De methode "berekenAantalZitplaatsenVrij" wordt gedeclareerd. Deze methode
    // geeft een "int" als antwoord en wordt derhalve als "int" gedeclareerd.
    public int berekenAantalZitplaatsenVrij() {
        int aantalZitplaatsenVrij = maximumZitplaatsen - zitplaatsenBezet;
        return aantalZitplaatsenVrij;
    }

}

In het voorbeeld worden de standaard waarden van het toerental en de verlichting ingesteld in de vorm van twee variabelen toerental en verlichtingAan. Het toerental staat standaard op nul en de verlichting staat standaard uit. De twee methoden starten() en getAantalZitplaatsenVrij() worden beschreven. De methode starten() beschrijft dat het toerental moet worden bijgesteld naar het minimum toerental en dat de verlichting ingeschakeld moet worden. Deze methode geeft geen antwoord, de element type void geeft dat ook aan. De methode getAantalZitplaatsenVrij() berekent het aantal zitplaatsen dat nog vrij is en beantwoordt dat in de vorm van een int, dat een cijfer kan bevatten.

Terug naar boven

Constructeur (constructor)

De constructeur is een speciale methode dat exact dezelfde naam heeft als de klasse, inclusief de hoofdletters. Deze methode wordt áltijd aangeroepen wanneer je een object van deze klasse wil aanmaken. De declaratie van een constructeur methode is overigens niet verplicht. Wanneer je geen constructeur declareert, dan maakt de compiler in de bytecode wel impliciet een lege constructeur aan.

// De klasse "Auto".
public class Auto {

    // De constructeur wordt gedeclareerd.
    public Auto() {
        maakAuto();
        aantalAutos++;
    }

}

In dit geval wordt eerst een nieuwe standaard auto aangemaakt. Na het aanmaken van de auto wordt de teller aantalAutos met 1 stap toegenomen.

Er kunnen meerdere constructeur methoden naast elkaar bestaan, elk afhankelijk van de invoer. Bijvoorbeeld:

// De klasse "Auto".
public class Auto {

    // De eerste constructeur wordt gedeclareerd.
    public Auto() {
        maakAuto();
        aantalAutos++;
    }

    // De tweede constructeur wordt gedeclareerd.
    public Auto(String merkType) {
        autoMerkType = merkType;
        maakAuto();
        aantalAutos++;
    }

}

De eerste constructeur maakt gewoon een standaard auto aan. Dit is handig wanneer je geen merk en type weet (of niet wilt opgeven) en toch een auto wil hebben. De tweede constructeur maakt een specifieke auto aan aan de hand van de opgegeven merk en type.

Terug naar boven

Instanties (instances)

Een object dat is gedeclareerd in de vorm van een variabele is een instantie. Deze variabele wordt een referentie-variabele genoemd; het refereert naar een object van een (vaak andere) klasse. Het is zeg maar een dynamische variabele, waarmee je alle toegankelijke methoden van die klasse kunt aanroepen. Je kunt vanuit een willekeurige klasse een object instantieren (aanmaken) als volgt:

// Een andere klasse.
public class AndereKlasse {

    // Het object wordt gedeclareerd als een referentie-variabele. De referentie-
    // variabele "standaardAuto" wordt bekend gemaakt als een instantie van de klasse
    // "Auto". De referentie-variabele "standaardAuto" is op dit moment nog leeg.
    Auto standaardAuto;

    // Het object wordt geinstantieerd (aangemaakt). De referentie-variabele
    // "standaardAuto" is nu gevuld met een standaard auto.
    standaardAuto = new Auto();

}

Dit kan ook korter in 1 regel en je kunt ook gebruik maken van een andere constructeur:

// Een andere klasse.
public class AndereKlasse {

    // Declareer de referentie-variabele "standaardAuto" en instantieer deze meteen
    // met een standaard auto.
    Auto standaardAuto = new Auto();

    // Declareer de referentie-variabele "alfaRomeo156" en instantieer deze meteen
    // met het gewenste auto. Dit maakt gebruik van de tweede constructeur, dat een
    // invoer van het merk en type accepteert.
    Auto alfaRomeo156 = new Auto("Alfa Romeo 156 1.9 JTD SW");

}
Terug naar boven

Referenties (references)

Een referentie is zoals hierboven uitgelegd dus een variabele dat naar een object van een klasse refereert. Je kunt meerdere referenties van één object hebben. Al deze referenties van één object bij elkaar worden aliasen genoemd.

// Een andere klasse.
public class AndereKlasse {

    // Instantieer de referentie "standaardAuto1".
    Auto standaardAuto1 = new Auto();

    // Declareer de referentie "standaardAuto2" als een alias van "standaardAuto1".
    Auto standaardAuto2 = standaardAuto1;

}

Je hebt nu dus twee identieke referenties van een standaard auto. Deze kunnen beiden vanaf het instantieren wel een eigen leven gaan leiden.

Terug naar boven

Aanroepen (to invoke)

Het aanroepen van de methoden van een object wordt gedaan met behulp van een punt (.). Wanneer je een auto wilt starten, dan moet je de methode starten() van de klasse Auto als volgt aanroepen:

// Een andere klasse.
public class AndereKlasse {

    // Declareer en instantieer de referentie "alfaRomeo156".
    Auto alfaRomeo156 = new Auto("Alfa Romeo 156 1.9 JTD SW");

    // Start de auto "alfaRomeo156"
    alfaRomeo156.starten();

}

Hiermee wordt dus de methode starten() van de klasse Auto aangeroepen:

// De klasse "Auto".
public class Auto {

    // De methode "starten".
    public void starten() {
        toerental = minimumToerental;
        verlichtingAan = true;
    }

}

De toerental wordt nu dus gelijkgesteld aan de minimum toerental van een Alfa Romeo 156 1.9 JTD SW. Tevens wordt de verlichting ingeschakeld.

Terug naar boven

Parameters (parameters, params)

De parameters van de methoden (hier vallen constructeurs dus ook onder) worden gebruikt om specifieke informatie te leveren, waarmee een methode verder kan werken. De parameters van een methode moet je tussen de haakjes definieren. Wanneer je een methode met de parameters wil aanroepen, dan moeten de element typen exact dezelfde zijn. Je kunt meerdere methoden maken met dezelfde benaming, maar met allemaal verschillende typen invoerparameters. Dit wordt overigens method overloading genoemd, zie ook Objecten - Methoden overladen.

// Een andere klasse.
public class AndereKlasse {

    // Declareer de referentie-variabele "standaardAuto" en instantieer deze meteen
    // met een standaard auto zonder invoerparameters.
    Auto standaardAuto = new Auto();

    // Declareer de referentie-variabele "alfaRomeo156" en instantieer deze meteen
    // met het gewenste auto dat je via de invoerparameters kunt invoeren.
    Auto alfaRomeo156 = new Auto("Alfa Romeo 156 1.9 JTD SW");

}

Hiermee worden in dezelfde volgorde dus de volgende constructeur methoden aangeroepen:

// De klasse "Auto".
public class Auto {

    // De eerste constructeur.
    public Auto() {
        maakAuto();
        aantalAutos++;
    }

    // De tweede constructeur dat een invoerparameter van het type String accepteert.
    public Auto(String merkType) {
        autoMerkType = merkType;
        maakAuto();
        aantalAutos++;
    }

}
Terug naar boven


In het volgende hoofdstuk wordt de grammatica van de Java code ontleed: Bouwstenen.

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

(C) April 2006, BalusC

Java tutorial - Bouwstenen

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

Grammatica van de Java code

Leestekens (lexical tokens)

Hieronder vallen benamingen (identifiers), sleutelwoorden (keywords), waarden (literals), operatoren (operators), witruimte (white space) en commentaren (comments). Het is een kunst om op basis hiervan een goede Java applicatie te kunnen schrijven.

Terug naar boven

Benamingen (identifiers)

In de Java code kun je de klassen, variabelen en methoden een (herkenbare) naam geven, zodat je ze elke keer eenvoudig kunt aanroepen. Deze namen worden benamingen genoemd.

Deze benamingen mogen alle letters en cijfers bevatten en bepaalde leestekens, zoals de underscore (_) en valuta symbolen (€, $, £, ¥, etc). Gereserveerde leestekens die onder operatoren vallen zijn vanzelfsprekend niet toegestaan in benamingen.

Er is slechts één voorwaarde: de benamingen mogen niet beginnen met een cijfer. Correcte benamingen zijn:

Auto, alfaRomeo156, $_1000, _één_auto

Ongeldige benamingen zijn:

156sportWagon, een-auto, Merk/Type, gewicht:kilo

Het gebruik van hoofdletters is ook erg belangrijk: alléén de benamingen van de klassen en de constructeur methoden mogen met een hoofdletter beginnen (het is dus niet verplicht, maar wel semantisch correct!). De benamingen van de variabelen en de methoden mogen niet met een hoofdletter beginnen (het is wel mogelijk, maar niet semantisch correct!). Wel kun je hoofdletters gebruiken om de leesbaarheid te bevorderen door van elk deelwoord de eerste letter een hoofdletter te maken. Dit noemen we CamelCase (kamelentoestand .. ). Bijvoorbeeld een variabele dat het merk en type van een auto moet voorstellen kun je autoMerkType noemen. Uiteraard kun je ook bijvoorbeeld auto_merk_type gebruiken, echter CamelCase is sneller te typen en prettiger te lezen.

Let wel, Java is hoofdlettergevoelig, dus de benamingen Auto en auto slaan op verschillende elementen!

Terug naar boven

Sleutelwoorden (keywords)

De sleutelwoorden zijn gereserveerde benamingen die dus niet gebruikt mogen worden om een klasse, variabele of methode te benoemen. Dit zou anders resulteren in compilatie fouten (problemen tijdens het omvormen van de Java code naar bytecode). De sleutelwoorden van Java in alfabetische volgorde zijn:

abstract, assert, boolean, break, byte, case, catch, char, class, continue, default, do, double, else, enum, extends, final, finally, float, for, if, implements, import, instanceof, int, interface, long, native, new, package, private, protected, public, return, short, static, strictfp, super, switch, synchronized, this, throw, throws, transient, try, void, volatile, while

Van de sleutelwoorden hierboven zijn strictfp, assert en enum kleine uitzonderingen. De eerste kwam pas sinds Java 1.2 in het pakket terecht, de tweede sinds Java 1.4 en de derde sinds Java 1.5, dus in de oudere versies zou je deze nog wel als een benaming kunnen gebruiken, maar dit is uiteraard af te raden. Java 1.2 en nieuwer wordt overigens ook wel Java 2 genoemd. Java 1.5 wordt ook wel Java 5.0 genoemd.

Naast dit allemaal zijn er ook nog twee gereserveerde benamingen die geen sleutelwoord-functie hebben, maar toch niet als een benaming gebruikt mogen worden, namelijk:

const, goto

Dit zijn sleutelwoorden die in veel andere programmeertalen worden gebruikt, maar niet in Java. Deze sleutelwoorden zijn gereserveerd om de eventuele verwarringen bij de programmeurs te voorkomen.

Tot slot zijn er ook nog drie gereserveerde waarden (literals) die als een sleutelwoord worden aangegeven. In de volgorde van "waar", "onwaar" en "leeg" zijn deze:

true, false, null

Diverse sleutelwoorden worden gespreid in alle volgende hoofdstukken uitgelegd: Datatypen, Operatoren, Toegangscontrole, Draaiboek, Foutenafhandeling en Objecten.

Terug naar boven

Waarden (literals)

Een waarde geeft de inhoud van een variabele aan. Dit kan van alles zijn: cijfers, karakters, booleanse waarden of willekeurige combinaties hiervan. Je kunt zelfs objecten in een variabele stoppen, het gaat hier dan om instanties in een referentie-variabele. Daarnaast is er ook de waarde null dat aangeeft dat een referentie-variabele leeg is (wel gedeclareerd, maar nog niet geinstantieerd).

// De variabele "jouwAuto" wordt gedeclareerd, echter deze is nog leeg.
String jouwAuto;

// De variabele "mijnAuto" wordt gedeclareerd en gevuld met "Alfa Romeo".
String mijnAuto = "Alfa Romeo";

// Laat de inhoud van deze variabelen zien:
System.out.println(jouwAuto); // Dit levert de waarde null op.
System.out.println(mijnAuto); // Dit levert de waarde Alfa Romeo op.

De soorten variabelen die geen objecten maar wel cijfers, karakters of booleanse waarden kunnen bevatten, staan in het hoofdstuk Datatypen uitgelegd.

Terug naar boven

Operatoren (operators)

Wanneer je een beetje goed hebt opgelet tijdens de reken- of wiskundelessen, dan weet je dat onder andere optel-, aftrek-, vermenigvuldig- en deeltekens onder operatoren vallen. Met operatoren kun je dus bepaalde rekenkundige danwel objectmatige handelingen uitvoeren met de variabelen danwel objecten.

De betekenis van verschillende operatoren worden uitvoerig behandeld in het hoofdstuk Operatoren.

Terug naar boven

Witruimte (white space)

De witruimte omvat onder andere spaties, tabs en enters. De spaties zijn noodzakelijk om de leestekens van elkaar te scheiden. De tabs zijn erg handig om de code leesbaar te houden door middel van inspringen op de juiste plaatsen. De enters zijn tevens erg handig om de code leesbaar te houden. Horizontaal scrollen is namelijk veel irritanter dan verticaal scrollen. Een goede leesbaarheid betekent meteen ook een snelle interpretatie en een goede onderhoudbaarheid.

Hoeveel witruimte je ook in de code zal toepassen, de code wordt er niet langzamer van. De compiler negeert de "overbodige" witruimte en perst de Java code gewoon bij elkaar in bytecode die prima leesbaar is voor de machines.

Terug naar boven

Commentaren (comments)

De commentaren zijn stukjes documentatie tussen de code door. Dat kan erg handig zijn om snel inzicht te kunnen krijgen wat de variabelen en de methoden "ook alweer" doen. Ook kan dit een voordeel zijn wanneer je de code van iemand anders probeert te doorgronden. Er zijn drie verschillende soorten commentaren:

/**
 * Dit is een Javadoc commentaar.
 *
 * Deze klasse maakt auto's aan. Wanneer je geen invoer parameters opgeeft,
 * dan wordt er automatisch een standaard auto gemaakt. Je kan ook een auto
 * merk/type opgeven als invoer parameter, dan wordt er een auto gemaakt
 * voldoend aan de merk/type.
 * <p>
 * Deze auto's kunnen starten, stoppen, gasgeven, schakelen en remmen. Je
 * kunt er ook individuen laten instappen en uitstappen. En je kunt ook het
 * verbruik, de snelheid en het aantal vrije zitplaatsen opvragen.
 * @author BalusC
 * @version 1.0
 */
public class Auto {

    // Dit is een een-regelige commentaar.

    // De eigenschappen die voor de hele klasse gelden.
    public static int aantalAutos = 0;

    /* Dit is een meer-regelige commentaar.
     *
     * Deze constructeur maakt automatisch een standaard auto aan en laat
     * de totale hoeveelheid auto's met 1 toenemen.
     */
    public Auto() {
        maakAuto();
        aantalAutos++;
    }

    /* Een meer-regelige commentaar kan ook hele blokken code uitschakelen.
    public Auto(String merkType) {
        autoMerkType = merkType;
        maakAuto();
        aantalAutos++;
    }*/

}

Een Javadoc commentaar begint met een /** en eindigt met */. Het is de bedoeling om daarin "nette" commentaar te plaatsen, want deze kan worden gebruikt door het Javadoc Tool om automatisch een nette HTML documentatie van de code te maken. Van Sun's eigen Java SE staat deze hier.

De een-regelige commentaar begint met een // en wordt vaak tussen de code door gebruikt als een beknopte uitleg van de functie van de variabelen en de methoden. In regel staat deze commentaar direct bovenaan de variabelen en de methoden. Sommige mensen plaatsen de een-regelige commentaar ook weleens aan het einde van een regel.

De meer-regelige commentaar begint met /* en eindigt met */ en wordt vaak gebruikt om bepaalde blokken code te "outcommenten" (uitschakelen), wat erg handig kan zijn bij het testen en debuggen.

Net als witruimte worden de commentaren ook volledig door de compiler genegeerd en dat levert dus geen performanceverlies op of iets dergelijks.

Terug naar boven


In het volgende hoofdstuk worden de verschillende soorten variabelen die verschillende soorten waarden mogen/moeten bevatten uitgelegd: Datatypen.

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

(C) April 2006, BalusC

Java tutorial - Datatypen

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

Informatiedragers van de Java code

Primitieve datatypen (primitive datatypes)

Java kent acht primitieve datatypen die géén objecten zijn: boolean, char, byte, short, int, long, float en double. Deze datatypen zijn puur bestemd om allerlei waarden te kunnen opslaan en rekenkundig ermee te kunnen werken met behulp van Operatoren.

Ofschoon deze datatypen geen objecten zijn, is er van elke datatype wel een zogenaamde wrapper object (omhulzen, omwikkelen): Boolean, Character, Byte, Short, Integer, Long, Float en Double. Met zo'n wrapper-object kun je o.a. de primitieve datatypen converteren naar strings (met de toString() methode) of omgekeerd (met o.a. de parse methoden). Ook kun je hiermee primitieve datatypen converteren naar wrapper objecten of omgekeerd. Zie ook Data conversies.

Terug naar boven

Initialiseren (to initialize)

Met initialiseren wijs je een waarde aan een vers gedeclareerde datatype variabele toe:

// Declareer een integer variabele. Deze is nu nog leeg.
int i;

// Hier wordt deze variabele pas geinitialiseerd.
i = 10;

// Dit kan ook tegelijk: declareren én initialiseren.
int j = 10;

Je kunt niet direct met een datatype variabele werken zonder deze eerst te initialiseren. Mocht je dat wel doen, dan krijg je in alle gevallen een foutmelding tijdens het compileren naar bytecode:

Unresolved compilation problem: The local variable i may not have been initialized

Maar een ongeinitialiseerde datatype variabele krijgt in specifieke gevallen wel automatisch een standaard waarde toegewezen. Zoals in het geval van een statische variabele en de variabelen van de instanties (dus niet de referentie-variabele zelf, maar de variabelen die in het geinstantieerde object zitten). Welke standaard waarde dit is, staat hieronder wel bij elke datatype vermeld. Let wel, een lege (ongeinitialiseerde) datatype variabele is niet gelijk aan null. Lege (ongeinstantieerde) referentie-variabelen zijn dat wel.

Terug naar boven

Boolean (boolean type)

De boolean datatype kan alleen worden gebruikt om de logische waarden true (waar) of false (onwaar) te bewaren. De boolean is géén nummeriek datatype, terwijl de overige primitieve datatypen dat wel zijn. De boolean wordt met name gebruikt om de flow (stroming, draaiboek) van de Java applicatie te aansturen (als, dan, of, als, dan, of, etc). Ze kunnen ook worden gebruikt voor variabelen die slechts 2 mogelijke waarden kunnen aangeven. Bijvoorbeeld:

// Declareer de boolean variabele die aangeeft of de verlichting aan is.
boolean verlichtingAan;

// De verlichting staat aan.
verlichtingAan = true;

// De verlichting staat uit.
verlichtingAan = false;

// Dit kun je dus ook gebruiken om de flow te aansturen.
if (verlichtingAan) {
    System.out.println("De verlichting staat aan.");
} else {
    System.out.println("De verlichting staat uit.");
}

De standaard waarde van een boolean is false.

Terug naar boven

Integers (integer types)

De integers omvatten de byte, short, int en long datatypen. Deze datatypen kunnen alleen gehele nummerieke waarden vasthouden. Het enige verschil tussen deze vier datatypen is de hoeveelheid geheugenruimte dat deze in beslag kan nemen en daaraan gerelateerd de maximum en minimum waarden die je kunt opgeven. Hieronder staat een overzichtje:

datatype breedte (bits) minimum waarde maximum waarde
byte 8 -27 (-128) 27-1 (127)
short 16 -215 (-32768) 215-1 (32767)
int 32 -231 (-2147483648) 231-1 (2147483647)
long 64 -263 (-9223372036854775808L) 263-1 (9223372036854775807L)

Om gelijk de eerste vraag te beantwoorden nadat je deze tabel uitgebreid hebt bestudeerd: van de positieve maximum waarden gaat er 1 van de waarde af, omdat de nul (0) ook meetelt als een positieve nummerieke waarde. En de tweede vraag zal vast over de postfix 'L' gaan die achter de long waarde staat: hiermee kun je expliciet aangeven dat het om een long gaat, anders wordt de waarde automatisch geconverteerd naar een int, de "standaard" integer datatype. Bij long waarden van kleiner dan -2147483648 of groter dan 2147483647 zou je anders de volgende foutmelding krijgen:

Unresolved compilation problem: <nummer> is out of range

In principe kun je een nummerieke waarde in alle soorten integers stoppen. Maar als je 100% van mening bent dat deze waarde gedurende de loop van de code nooit groter dan 127 of kleiner dan -128 wordt, dan kun je deze waarde ook gewoon in een byte zetten. Hoe kleiner de datatype, hoe minder geheugenruimte je in beslag neemt. Al is dat vrij fractioneel. De "standaard" integer datatype is de int.

Let wel, de integers rekenen weer bij het begin af door wanneer de maximum waarde wordt overschreden:

// Declareer en initialiseer de verschillende integers.
byte b = 100;
short s = 30000;
int i = 2000000000;
long l = 9000000000000000000L;

// Nu laten we hun met zichzelf optellen en de uitkomst geven.
System.out.println(b += b);
System.out.println(s += s);
System.out.println(i += i);
System.out.println(l += l);

Met als uitkomst:

-56
-5536
-294967296
-446744073709551616

Voor de byte wordt dit even uitgelegd: 100 + 100 = 200. Echter bij de byte wordt er na 127 teruggegaan naar -128 en daarvanuit doorgeteld. Dus wanneer we bij 128 willen zijn, komen we in -128 terecht. Er rest dus nog 200 - 128 = 72, dan tellen we vanaf -128 door met 72: -128 + 72 = -56.

Let wel: b + 100 levert daarentegen wél 200 op, want de "standaard" integer datatype is de int. Wanneer je een integer waarde niet specifiek declareert, dan wordt er dus altijd van een int uitgegaan. Omdat 100 een int is, wordt de b automatisch geconverteerd naar een int. Om dit te voorkomen moet je casten: (byte) (b + 100). Het casten wordt later in deze tutorial uitgelegd: Objecten - Objecten converteren.

De standaard waarde van de integers is 0 (nul).

Terug naar boven

Floating points (floating-point types)

De floating points omvatten de float en double datatypen. Deze datatypen kunnen gebroken nummerieke waarden vasthouden. Het enige verschil tussen deze twee datatypen is de hoeveelheid geheugenruimte dat deze in beslag kan nemen en daaraan gerelateerd de maximum en minimum waarde die je kunt opgeven. Hieronder staat een overzichtje:

datatype breedte (bits) minimum waarde maximum waarde
float 32 ±1.40129846432481707 x 10-45 ±3.40282346638528860 x 1038
double 64 ±4.94065645841246544 x 10-324 ±1.79769313486231570 x 10308

Deze minimum en maximum waarden zijn dus gelijk voor de positieve en de negatieve waarden. De nul kan zowel positief als negatief zijn.

Bij de float en double moet je alle gehele nummerieke waarden specifiek aangeven met een postfix 'F' respectievelijk 'D' of specifiek casten naar float danwel double, anders worden deze waarden aangenomen als een int. Dit is niet nodig bij gebroken nummerieke waarden, deze worden standaard een float. Het casten wordt overigens later in deze tutorial uitgelegd: Objecten - Objecten converteren.

double d1 = 1 / 3; // Gehele nummerieke waarden zijn standaard 'int'.
double d2 = 1D / 3D; // Specifieke postfix met 'D'.
double d3 = 1.0 / 3.0; // Gebroken nummerieke waarden zijn standaard 'float'.
double d4 = (double) 1 / 3; // Specifieke cast naar 'double'.

// Laat de waarden zien.
System.out.println(d1);
System.out.println(d2);
System.out.println(d3);
System.out.println(d4);

Met als uitkomst:

0.0
0.3333333333333333
0.3333333333333333
0.3333333333333333

De standaard waarde van de floating points is +0.0F voor de float en +0.0D voor de double.

Terug naar boven

Karakter (character type)

De char datatype is 16 bits breed en kan 65536 (216) verschillende Unicode karakters vasthouden. Let wel, niet allemaal tegelijk, maar slechts 1 tegelijk! Niet alleen onze Alfabet komt erin voor, maar ook heel veel Aziatische schriften (Chinees, Koreaans, Japans, etc). Het laatste neemt zo'n 70% van de huidige Unicode karakterset in beslag.

Je kunt de char vullen met een letter of een Unicode code voorafgegaan door een backslash "\". De hoofdletter "A" heeft een Unicode code van "u0041". De char datatype vereist dat je de waarde met enkelvoudige aanhalingstekens ' (single quotes) omvat:

// Declareer en initialiseer de karakters.
char a = 'A';
char b = '\u0041';

// Vergelijk de twee karakters.
if (a == b) {
    System.out.println("Ze zijn hetzelfde.");
} else {
    System.out.println("Ze zijn niet hetzelfde.");
}

En jawel hoor, de uitkomst is:

Ze zijn hetzelfde.

De standaard waarde van een char is '\u0000' (de Unicode code voor "leeg").

Terug naar boven

En de String?

Nee, de string is geen datatype. Het is een object. Op zich is dat eigenlijk ook logisch, want je kunt helemaal niet rekenen met strings. De String is een speciale object dat alle mogelijke waarden kan opslaan en dan wel puur voor de opslag. De String is een bijzondere object dat niet direct geinstantieerd hoeft te worden met de new sleutelwoord. De String wordt gekenmerkt door een waarde dat is omsloten door dubbele aanhalingstekens " (double quotes). De Java Virtual Machine maakt impliciet een nieuwe String object aan voor alles wat tussen dubbele aanhalingstekens staat. Je kan een String dus op de volgende manieren gebruiken:

// Declareer, instantieer en initialiseer volgens de gebruikelijke manier.
String str1 = new String();
str1 = "test1";

// Declareer, instantieer en initialiseer tegelijk.
String str2 = new String("test2");

// Declareer en initialiseer zoals bij de datatypen.
String str3 = "test3";

Het behoeft geen uitleg dat de laatste manier de meestgebruikte manier is. De eerste twee manieren zijn af te raden, omdat je hier in wezen twéé String objecten aanmaakt (één met de new String() en één met de waarde tussen de dubbele aanhalingstekens), wat alleen maar extra geheugenruimte kost.

Aangezien de String een object is, is de standaard waarde natuurlijk null.

Terug naar boven


In het volgende hoofdstuk wordt uitgelegd op welke manieren je allemaal kunt werken met de hierboven genoemde datatypen en diverse objecten: Operatoren.

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

(C) April 2006, BalusC

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