Saturday, April 1, 2006

Java tutorial - Toegangscontrole

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

Verkeersregelaars van de Java code

Pakketen (packages)

De pakketen (hierna packages genoemd) vormen een soort directorystructuur voor de Java code. Je kunt alle klassen naar wens in de packages groeperen en uitsorteren, net zoals je wellicht je downloads, muziek- en videobestanden op een logische manier groepeert en uitsorteert over directories. De namen van de packages worden structureel gescheiden door een punt . net zoals de slash / dat doet bij de directories. De packages behoor je van groot naar klein te benoemen, bijvoorbeeld: nl.balusc.voertuigen. Een soort van een omgekeerde domeinnaam uit een internetadres dus. De package behoor je in de Java code als package te declareren en dan wel helemaal bovenaan, in de eerste regel code.

package nl.balusc.voertuigen; // De package declaratie.

public class Auto {

    public Auto() {
        maakAuto();
        aantalAutos++;
    }

}

De volledige naam van de klasse Auto wordt dan in feite nl.balusc.voertuigen.Auto. Je zou deze klasse als volgt vanuit een andere klasse in een andere package kunnen importeren:

package nl.andere.package; // De package declaratie.

import nl.balusc.voertuigen.Auto; // Importeer de klasse Auto.

public class AndereKlasse {

    // Declareer en instantieer een standaard auto.
    Auto standaardAuto = new Auto();

}

De import behoor je te declareren tussen de package declaratie en de class declaratie. Een import is niet nodig wanneer de klasse in dezelfde package zit. Daarbuiten is een import wel noodzakelijk.

Je kan de import declaratie ook met een wildcard * gebruiken. Daarmee geef je aan dat álle klassen van desbetreffende package geimporteerd moeten worden. Let wel: daarmee importeer je niet alle onderliggende of bovenliggende packages! Bijvoorbeeld:

package nl.andere.package; // De package declaratie.

import nl.balusc.voertuigen.*; // Importeer alle klassen van desbetreffend package.

public class AndereKlasse {

    // Declareer en instantieer een standaard auto.
    Auto standaardAuto = new Auto();

}

Let wel, alle klassen van de java.lang package wordt standaard geïmporteerd in elke klasse, zoals Object, String, System, etc. Dus je hoeft deze niet specifiek te importeren.

Terug naar boven

Toegangsmodifiers (accessibility modifiers)

Je hebt niet zomaar toegang tot alle klassen danwel diens methoden en/of variabelen. Dit wordt geregeld door de toegangsmodifiers die er in verschillende smaken zijn. De toegangsmodifiers public, protected en private regelen de zichtbaarheid vanuit andere klassen. De toegangsmodifiers static, final, abstract en native regelen het gedrag ten opzichte van andere klassen. De toegangsmodifiers strictfp, transient, volatile en synchronized zorgen tenslotte voor het gedrag tijdens de runtime. Wanneer je een toegangsmodifier wil gebruiken, dan moet je de declaratie altijd hiermee beginnen. Hieronder staat een samenvatting van alle modifiers en hun eventuele invloed op de klassen, variabelen en methoden:

modifier klasse variabele methode
geen modifier
(zichtbaarheid)
De klasse is alleen toegankelijk voor klassen in dezelfde package. De variabele is alleen toegankelijk voor klassen in dezelfde package. De methode is alleen toegankelijk voor klassen in dezelfde package.
public
(zichtbaarheid)
De klasse is overal toegankelijk. De variabele is overal toegankelijk. De methode is overal toegankelijk.
protected
(zichtbaarheid)
nvt De variabele is alleen toegankelijk voor klassen in dezelfde package en voor alle subklassen, ongeacht de package. De methode is alleen toegankelijk voor klassen in dezelfde package en voor alle subklassen, ongeacht de package.
private
(zichtbaarheid)
nvt De variabele is alleen toegankelijk binnen eigen klasse en onzichtbaar voor alle andere klassen, ongeacht de package. De methode is alleen toegankelijk binnen eigen klasse en onzichtbaar voor alle andere klassen, ongeacht de package.
static
(objectmatig)
nvt De variabele is hetzelfde voor alle instanties van de klasse. De methode is hetzelfde voor alle instanties van de klasse.
final
(objectmatig)
De klasse kan niet meer worden uitgebreid, het kan geen subklassen meer hebben. De waarde van de variabele kan niet meer worden gewijzigd. In regel gaat het hier om constanten. De methode kan niet meer worden overgeschreven (overriden) door een subklasse.
abstract
(objectmatig)
De klasse kan abstracte methoden bevatten en kan niet geinstantieerd worden als een object. Het kan wel uitgebreid worden naar een subklasse. nvt De methode is een interface (het heeft geen inhoud). Bij ten minste een abstracte methode is de klasse verplicht ook abstract te zijn.
native
(objectmatig)
nvt nvt De methode is een interface (het heeft geen inhoud) en is toegankelijk voor een andere taal (C#, C++, etc).
strictfp
(runtime)
In de hele klasse zijn alle tussentijdse berekeningen met floating-points (float en double) ook echt volledig floating-point. nvt In de hele methode zijn alle tussentijdse berekeningen met floating-points (float en double) ook echt volledig floating-point.
transient
(runtime)
nvt De waarde van de variabele is aan veranderingen onderhevig, de waarde mag niet bevroren (persistent) worden wanneer een ongebruikte object voor een bepaalde tijd in de koelkast gaat (to be serialized). nvt
volatile
(runtime)
nvt De waarde van de variabele is aan sterke veranderingen onderhevig, de waarde mag niet gecached worden voor een volgende thread (aanroep). nvt
synchronized
(runtime)
nvt nvt De methode kan slechts éénmaal tegelijk worden aangeroepen, ofwel het gedraagt zich singlethreaded in plaats van multithreaded.

Je kunt ook meerdere toegangsmodifiers aan een declaratie toewijzen. Echter niet alle combinaties zijn zomaar mogelijk. Er zijn toegangsmodifiers die niet tegelijkertijd gebruikt mogen worden. Hieronder staat beschreven waaruit je een keuze moet maken voor het geval dat:

Klassen: final óf abstract.

Variabelen en methoden: public óf protected óf private.

Methoden: final óf abstract óf native óf final native.

Methoden: synchronized kan niet in combinatie met abstract.

De volgorde waarin je meerdere toegangsmodifiers aan een declaratie toewijst maakt in principe niet uit, maar voor de snelle leesbaarheid wordt in het algemeen ervan uitgegaan dat je de volgorde gebruikt zoals in de tabel hierboven staat.

Terug naar boven

Klasse toegangsmodifiers

Er zijn vier klasse toegangsmodifiers en dan wel public, final, abstract en strictfp. Hieronder staan voorbeelden van de toegangsmodifiers.

// Deze klasse is overal toegankelijk (public) en deze klasse kan in feite
// alleen aangeroepen worden via subklassen (abstract). De klasse is in
// regel de absolute basis-klasse, waarop verder kan worden uitgebouwd.
// Alle subklassen moeten verplicht de abstract gedeclareerde methoden
// van de klasse bevatten.
public abstract class Voertuigen {
    ...
}

// Deze klasse is alleen toegankelijk voor de klassen in dezelfde package
// en deze klasse kan in feite alleen aangeroepen worden via subklassen
// (abstract). Zo'n klasse is dan een zogenaamde blauwdruk (basis-klasse)
// waarop dan verder kan worden uitgebouwd. Alle subklassen moeten verplicht
// de abstract gedeclareerde methoden van de klasse bevatten.
abstract class AlfaRomeo156 {
    ...
}

// Deze klasse is overal toegankelijk (public). Dit is in regel de 
// hoofd-klasse die overal aangeroepen moet kunnen worden.
public class Auto {
    ...
}

// Deze klasse is overal toegankelijk (public) en deze klasse kan niet
// meer worden uitgebreid met subklassen (final). Zulke klassen zijn
// dan het uiterste eindpunt van de abstracte structuur.
public final class AlfaRomeo156_19JTDSW {
    ...
}

// Deze klasse is alleen toegankelijk voor de klassen in dezelfde package
// (geen modifier). Dit wordt vaak gebruikt voor groep-specifieke klassen.
class AlfaRomeo156Opties {
    ...
}

// Deze klasse is alleen toegankelijk voor de klassen in dezelfde package
// en deze klasse kan niet meer worden uitgebreid met subklassen (final).
// Dit is vaak een groep-specifieke eindpunt.
final class AlfaRomeo156Opties_Sportpakket {
    ...
}

// Deze klasse kan berekeningen met floating-points bevatten. Deze kunnen
// in sommige gevallen voor geheugenproblemen zorgen. Sinds Java 1.2 is dit
// hersteld door de tussentijdse berekeningen niet volledig floating-point
// te laten verlopen. Het antwoord is overigens wel volledig floating-point.
// Met deze modifier kun je aangeven dat de berekeningen echt wel volledig
// in floating-point moeten verlopen. Het nut van deze modifier staat nog
// ter discussie.
strictfp class BerekenSnelheid {
    ...
}

// Deze klasse kan niet meer worden uitgebreid met subklassen (final)
// en deze klasse kan in feite alleen aangeroepen worden via subklassen
// (abstract). Met logisch nadenken zou je concluderen dat deze klasse
// dan nutteloos zou zijn. Deze constructie is dus niet toegestaan.
final abstract class Klasse1 {
    ...
}

// Ook niet in combinatie met een zichtbaarheids modifier.
public final abstract class Klasse2 {
    ...
}
Terug naar boven

Variabele toegangsmodifiers

Er zijn zeven variabele toegangsmodifiers: public, protected, private, static, final, transient en volatile. Hieronder staan voorbeelden van de toegangsmodifiers.

public class Klasse {

    // Deze variabele is alleen toegankelijk voor de klassen in dezelfde
    // package (geen modifier).
    int i1;

    // Deze variabele is overal toegankelijk (public).
    public int i2;

    // Deze variabele is alleen toegankelijk voor de klassen in dezelfde
    // package en voor alle subklassen, ongeacht de package (protected).
    protected int i3;

    // Deze variabele is alleen toegankelijk binnen eigen klasse en
    // onzichtbaar voor alle andere klassen, ongeacht de package (private).
    private int i4;

    // De zichtbaarheid modifiers mogen niet gemixt worden.
    public protected private int i5;

    // Deze variabele is hetzelfde voor alle instanties van de klasse (static).
    // Als deze variabele verandert, dan geldt de verandering dus direct voor
    // alle instanties van de klasse. Een soort Global Variabele dus, wat handig
    // kan zijn voor counters.
    static int aantalAutos;

    // De waarde van deze variabele kan niet meer veranderd worden (final).
    // Voor een Klasse-variabele is het verplicht om deze gelijk te
    // initialiseren. Dit wordt in regel alleen voor constanten gebruikt.
    // In verband met de herkenbaarheid worden de benamingen van de final
    // variabelen in regel met hoofdletters geschreven.
    final double PI = 3.14159265358979323846264338327950288419716939937510;

    // De waarde van deze variabele is aan veranderingen onderhevig en mag
    // niet bevroren worden wanneer een ongebruikte object voor een bepaalde
    // tijd in de koelkast gaat (transient). Bijvoorbeeld: een auto die zich
    // al een hele tijd in exact dezelfde toestand bevindt wordt als een
    // "ongebruikt object" in de koelkast geplaatst. En de koelvloeistof
    // temperatuur loopt het risico om onverwacht zonder opgaaf van reden
    // (zonder een commando van buitenaf) gewijzigd te worden.
    transient float koelvloeistofTemperatuur;
 
    // De waarde van deze variabele is aan sterke veranderingen onderhevig
    // en mag niet gecached worden voor een volgende thread/aanroep (volatile).
    // Dit heeft slechts een voordeel op de geheugengebruik. Het maakt de code
    // niet trager, immers de variabele wordt in principe toch al voortdurend
    // gewijzigd. Bijvoorbeeld een autoklok (slecht voorbeeld, maar het geeft
    // je een idee; deze modifier wordt sowieso erg weinig gebruikt).
    volatile Date autoKlok;

}

Let op: binnen een methode kan je een variabele alléén als final declareren, of gewoon zonder toegangsmodifiers. De variabelen die binnen de methode worden gedeclareerd zijn namelijk standaard onzichtbaar buiten de methode.

public class Klasse {

    public void methode() {

        // De standaard declaratie zonder toegangsmodifiers.
        int i1;

        // De declaratie met de final toegangsmodifier. Voor een final
        // methode-variabele is een initialisatie niet verplicht, in
        // tegenstelling tot de final klasse-variabele.
        final int INT2;

        // Alle andere modifiers zijn niet toegestaan.
        public int i3;
        protected int i4;
        private int i5;
        static int i6;
        transient int i7;
        volatile int i8;
    }

}

Let op: binnen een interface zijn alle variabelen per definitie als public static final gedeclareerd. Je bent dankzij de final toegangsmodifier dan ook verplicht om deze gelijk te initialiseren. Je kunt geen andere toegangsmodifiers gebruiken voor een variabele binnen een interface.

public interface Interface {

    // Al de volgende declaraties zijn impliciet public static final,
    // ongeacht de gebruikte toegangsmodifier(s).
    int INT1 = 1;
    public int INT2 = 2;
    static int INT3 = 3;
    final int INT4 = 4;
    public static int INT5 = 5;
    public final int INT6 = 6;
    static final int INT7 = 7;
    public static final int INT8 = 8;

    // Alle andere modifiers zijn niet toegestaan.
    protected int i4;
    private int i5;
    transient int i7;
    volatile int i8;

}

De static modifier kun je ook als een code blok gebruiken (static initializer block). Alle binnen de static blok gedeclareerde variabelen worden dan als eerste geinitialiseerd bij een instantie. Let wel, de variabelen zijn zélf niet static! Net als binnen een methode kun je binnen een static blok de variabelen alléén declareren als final of zonder modifier.

public class Klasse {

    // De static blok. Deze wordt als het eerste geinitialiseerd wanneer
    // de klasse wordt geinstantieerd. Je kunt er meerdere in je code
    // hebben. Ze worden dan gewoon in de leesvolgorde uitgevoerd.
    static {
        int i1 = 10;
        final int INT2 = 50;
    }

}
Terug naar boven

Methode toegangsmodifiers

Er zijn negen methode toegangsmodifiers: public, protected, private, static, final, abstract, native, strictfp en synchronized. Hieronder staan voorbeelden van de toegangsmodifiers.

public class Klasse {

    // Deze methode is alleen toegankelijk voor de klassen in dezelfde
    // package (geen modifier).
    void methode1() {
        ...
    }

    // Deze methode is overal toegankelijk (public).
    public void methode2() {
        ...
    }

    // Deze methode is alleen toegankelijk voor de klassen in dezelfde
    // package en voor alle subklassen, ongeacht de package (protected).
    protected void methode3() {
        ...
    }

    // Deze methode is alleen toegankelijk binnen eigen klasse en
    // onzichtbaar voor alle andere klassen, ongeacht de package (private).
    private void methode4() {
        ...
    }

    // De zichtbaarheid modifiers mogen niet gemixt worden.
    public protected private void methode5() {
        ...
    }

    // Deze methode is hetzelfde voor alle instanties van de klasse (static).
    // Een soort Global Methode dus, wat handig kan zijn voor methoden die
    // zich identiek kunnen gedragen voor elke instantie van de klasse. Dit
    // soort methoden zijn handig om bijvoorbeeld een configuratiebestand
    // te uitlezen en de waarden in het geheugen te opslaan; om een SELECT of
    // INSERT SQL statement te uitvoeren (de statement geef je uiteraard als
    // een parameter op); om statusmeldingen naar de systeemconsole te
    // schrijven, etcetera.
    static void methode6() {
        ...
    }

    // Deze methode kan niet meer worden overgeschreven (overriden) door
    // een subklasse (final). In regel kun je in een subklasse de methode
    // van de superklasse overschrijven door gewoon exact dezelfde naam
    // en de type parameters te gebruiken. Dit kan handig zijn in gevallen
    // wanneer je iets toch nét anders wil doen ten opzichte van
    // de superklasse.
    final void methode7() {
        ...
    }

    // Deze methode is een interface (abstract). Zo'n methode beschrijft
    // dan de naam en de eventuele parameters die een subklasse verplicht
    // zou moeten hebben.
    abstract void methode8();

    // Deze methode is een interface dat toegankelijk is voor andere
    // programmeertalen (native). Het komt erop neer dat je dan aan de
    // de andere programmeertaal kunt vertellen welke Java methoden
    // allemaal beschikbaar zijn om aangeroepen mogen worden.
    native void methode9();
 
    // Deze methode kan berekeningen met floating-points bevatten. Deze
    // kunnen in sommige gevallen voor geheugenproblemen zorgen. Sinds
    // Java 1.2 is dit hersteld door de tussentijdse berekeningen niet
    // volledig floating-point te laten verlopen. Het antwoord is overigens
    // wel volledig floating-point. Met deze modifier kun je aangeven dat
    // de berekeningen echt wel volledig in floating-point moeten verlopen.
    // Het nut van deze modifier staat nog ter discussie.
    strictfp void methode10() {
        ...
    }

    // Deze methode kan slechts éénmaal tegelijk worden aangeroepen, ofwel
    // het gedraagt zich singlethreaded in plaats van multithreaded
    // (synchronized). Dit wordt vooral gebruikt bij acties die echt maar
    // één keer tegelijk uitgevoerd mag worden. Bijvoorbeeld: een specifiek
    // bestand wijzigen, een specifiek UPDATE SQL statement uitvoeren, etcetera.
    synchronized void methode11() {
        ...
    }

    // De volgende combinaties zijn niet toegestaan, ongeacht de gebruikte
    // zichtbaarheids modifier (public, protected of private).
    final abstract void methode12();

    abstract synchronized void methode13();

}
Terug naar boven

Nog even over de static modifier

Om vanuit een andere klasse een static variabele of methode van een klasse te kunnen aanroepen is het niet verplicht om deze klasse als een op zichzelf staand object te instantieren. Immers, de static variabelen en methoden gedragen zich toch hetzelfde voor alle instanties van de klasse. Een voorbeeld van een klasse met een static variabele en methode:

public class Klasse {

    // Declareer de static variabele.
    public static int counter;

    // Declareer de static methode.
    public static void methode() {
        ...
    }

}

Je zou deze dus als volgt vanuit een andere klasse kunnen aanroepen zonder de klasse specifiek te declareren en instantieren:

public class AndereKlasse {

    public void methode() {

        // Benader de static variabele zonder te declareren en instantieren.
        Klasse.counter++;

        // Benader de static methode zonder te declareren en instantieren.
        Klasse.methode();
    }

}
Terug naar boven


In het volgende hoofdstuk staat beschreven hoe je het verloop van de Java code kunt beheersen: Draaiboek.

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

(C) April 2006, BalusC

3 comments:

M said...

Bedankt voor de heldere uitleg!

michel said...

erg nuttige info!

Flaffy said...

SUPER ;D

Nu ga ik het niet in van die moeilijke cursussen moeten leren volgend semester...

Er staan ook veel handigheden die je een 1ste x nog niets zeggen, maar wanneer je het herleest wel handig kunnen zijn voor prestatiewinst enz. !