Deel 1 : Basisprincipes van objectoriëntatie

advertisement
Deel 1 : Basisprincipes van objectoriëntatie
HFST 1
-
een instantie = een bepaald object
namen van klassen  hoofdletter
namen van objecten  kleine letter
parameters = de extra waarden die bij sommige methodes ingevoerd moeten worden
een type geeft aan welke soort gegevens geschikt zijn als paramters
HFST 2
-
-
-
-
-
-
public class Naamvandeklasse
{
…
}
de inhoud van een klasse samengevat:
1) de velden bevatten gegevens die het object moet gebruiken
2) met de constructors kan een object op de juiste manier worden ingesteld op het
moment dat het wordt aangemaakt
3) met de methodes wordt het gedrag van de objecten geïmplimenteerd
een formele parameter is alleen beschikbaar voor een object binnen de body van een
constructor of methode die deze declareert. Het bereik van een parameter is beperkt
tot de body van de constructor of methode waarin deze wordt gedeclareerd.
Het bereik van een veld daarentegen is de hele klassedefinitie
De levensduur van een parameter is beperkt tot 1 aanroep van een constructor of
methode
De levensduur van een veld daarentegen is gelijk aan de levensduur van het object
waartoe het behoort
het is een absolute regel in Java dat een constructor geen returntype mag hebben
accessor = een methode die informatie over de toestand van een object teruggeeft
mutator = een methode die de toestand van een object verandert
conditioneel statement :
if (voer een test uit die alleen waar of niet waar
uitkomt)
{
voer statements uit als resultaat waar was
}
else
{
voer deze statements uit als resultaat niet waar was
}
net als formele parameters hebben lokale variabelen een bereik dat beperkt is tot de
statements van de methode waartoe ze behoren
kenmerken van variabelen: (velden, formele en lokale)
1) alle 3 variabelen kunnen een waarde opslaan die overeenkomt met het voor hen
gedefinieerde type
2) velden worden buiten constructors en methodes gedefiniëerd
3) velden worden gebruikt om gegevens op te slaan die gedurende de hele
levensduur van een object bestaan. Op die manier beschrijven ze de huidige
toestand van een object.
4) Velden werken klassebreed; ze zijn benaderbaar vanuit de hele klasse en
kunnen dus worden gebruikt binnen alle constructors en methodes van de klasse
waarin ze gedefinieerd zijn.
5) Zolang ze als private gedefinieerd zijn, kunnen velden niet van buiten de klasse
waarin ze gedefinieerd zijn benaderd worden.
6) Formele parameters en lokale variabelen bestaan alleen tijdens de periode dat
een constructor of methode wordt uitgevoerd.
7) Formele parameters worden gedefinieerd in de header van een constructor of
methode. Ze krijgen hun waarde van buitenaf en worden geïnitialiseerd door de
actuele parameterwaarden die een onderdeel zijn van de constructor of methodeaanroep.
8) Formele parameters hebben een bereik dat beperkt is tot het bereik van de
constructor of de methode waarin ze gedefinieerd wordt.
9) Lokale variabelen worden gedefinieerd binnen de body van een constructor of
methode. Ze kunnen alleen binnen de body van de constructor of methode
gebruikt en geïnitialiseerd worden. Lokale variabelen moeten geïnitialiseerd
worden voordat ze in een expressie worden gebruikt; ze hebben dus geen
standaardwaarden.
10) Lokale variabelen hebben een bereik dat beperkt is tot het blok waarin ze
gedefinieerd zijn. Ze kunnen niet van buiten dat blok benaderd worden.
HFST 3
-
-
-
Java kent 2 sterk verschillende types:
a) primitieve types: worden vooraf gedefinieerd in Java (opgeslaan in een
variabele)
b) objecttypes: worden gedefinieerd door klassen (opgeslaan in een
verwijzing naar een object)
logische operatoren:
= gebruiken de boolean waarden als argument en produceren een nieuwe boolean
waarde als resultaat.
en
&&
of
||
niet
!
integer convergeren in een String:
“” + getal
de syntax van een externe methode-aanroep:
object.methodenaam ( parameters );
de syntax van een interne methode-aanroep:
methodenaam ( parameter );
HFST 4
-
gebruiken van een Arraylist:
import java.util.ArrayList
{
private ArrayList … ;
…
{
a = new ArrayList () ;
}
…
{
-
-
-
-
-
-
-
a.add( a );
}
// we willen de grootte weten
{
return a.size ();
}
//we willen er eentje zien (welke)
{
System.out.println ( a.get( welke ) );
}
3 belangrijke kenmerken van een ArrayList
1) de klasse kan de interne capaciteit op verzoek aanpassen: wanneer er items aan
toegevoegd worden maakt de klasse er gewoon ruimte voor
2) de klasse houdt een eigen teller van het huidige aantal opgeslagen items. De
methode size () retourneert het aantal opgeslagen objecten.
3) De klasse houdt ook de volgorde van de opgeslagen items bij. Je kunt ze later in
dezelfde volgorde ophalen.
om iets te verwijderen uit een ArrayList
a.remove ( index );
 de indexnummers van de overgebleven items verschuiven met 1 eenheid
while – lus
while ( loop conditie )
{
loop body;
}
een Iterator gebruiken
import java.util.Iterator;
…
// toont alle objecten uit de ArrayList
{
Iterator = a.iterator ();
while (it.hasNext () )
{
System.out.println ( it.next () );
}
}
het begrip null is een speciale waarde in Java met de betekenis ‘geen object’
Casten:
Een cast bestaat uit de naam van een type die tussen een paar haakjes geschreven
wordt.
Casten wordt veel gebruikt wanneer objecten uit een collectie worden opgehaald.
Casten is noodzakelijk omdat het mogelijk is om elk type in een collectie op te slaan.
We moeten de compiler dus duidelijk maken welk type object we ophalen.
collectie met een vaste omvang : Array
hoewel de onveranderlijke omvang van arrays in veel situaties een belangrijk nadeel
is, hebben ze tenminste 2 voordelen t.o.v. collectieklassen met een veranderlijke
omvang:
1) het benaderen van items in een array is vaak efficiënter dan het
benaderen van items in een vergelijkbare collectie met veranderlijke
omvang
2) arrays kunnen objecten of waarden van het primitieve type bevatten.
Collecties met veranderlijke omvang kunnen alleen objecten bevatten
gebruik van Arrays
…
-
private int [] a;
…
{
a = new int [ getal ];
}
// de lengte bepalen
{
return a.length;
}
for – lus
met name handig als
i.
we enkele statements een bepaald aanteel keer willen
uitvoeren
ii.
we een variabele binnen de lus nodig hebben waarvan de
waarde steeds een regelmatig aantal verandert (meestal + 1)
for ( initialisering; conditie; actie na de body)
{
te herhalen statements;
}
HFST 5
-
-
-
-
-
String:
trim ()
doet spaties aan begin en einde weg
length ()
geeft lengte terug
toLowerCase ()
zet alles in kleine letters om
equals ()
vergelijkt
startsWith ()
begint met…
de klasse Random ()
import java.util.Random ;
Random rg ;
rg = new Random ;
int getal = rg.nextInt (eventueel een maximum);
public vs. private
i.
public geeft aan dat een constructie van een klasse, veld of methode
voor iedereen zichtbaar is (= een onderdeel van de interface is)
ii.
private geeft aan dat het niet zichtbaar is van buitenaf (=een
onderdeel van de implementatie is)
het begrip static
i.
met het begrip static worden klassevariabelen in Java gedefinieerd.
Klassevariabelen zijn velden die opgeslagen zijn in een klasse zelf en
niet in een object
ii.
klassevariabelen worden vaak gebruikt als een waarde altijd voor alle
instanties van een klasse gelijk moet zijn
constanten
i.
het begrip static word vaak gebruikt voor het definiëren van
constanten. In Java worden deze gedefinieerd met het begrip final:
private final int a = …;
ii.
als een waarde niet mag veranderen is het een goed ideee om deze
final te declareren. Daardoor wordt het onmogelijk om de waarde
achteraf te veranderen. Elke poging om een veld met een constante te
veranderen zal resulteren in een foutmelding bij het compileren.
-
in de praktijk komt het vaak voor dat constanten in alle instanties van een klasse
gelden. In dat geval declareren we klasseconstanten als volgt
private static final int a = …;
HFST 6
-
elke afzonderlijke methode kan, zodra die geschreven en gecompileerd is getest
worden
belangrijk bij het testen is om ervoor te zorgen dat de begrenzingen worden
gecontroleerd
bij een regressietest wordt een test die eerder met succes werd uitgevoerd, opnieuw
uitgevoerd om er zeker van te zijn dat de nieuwe versie ook met succes kan werken.
HFST 7
afhankelijkheid = de onderlinge verbondenheid van klassen (we streven naar een
zwakke afhankelijkheid).
- Cohesie = het aantal en de diversiteit van de taken waarvoor 1 afgebakend stuk van
een toepassing verantwoordelijk is
- Dupliceren van code is een kenmerk van een slecht ontwerp
- Inkapselingsregel (de informatie over de implementatie verbergen) stelt dat alleen
informatie over wat een klassse kan doen, zichtbaar moet zijn en niet hoe ze dit doet.
- Ontwerpen op basis van verantwoordelijkheid: elke klasse moet zorgen voor de
verwerking van de eigen gegevens.
- We proberen een klasseontwerp te maken dat later gemakkelijk gewijzigd kan
worden omdat de effecten van de wijziging goed te traceren zijn.
- Slecht: impliciete afhankelijkheid is een situatie waarbij een klasse afhankelijk is van
interne informatie van een andere klasse, maar waarbij deze afhankelijkheid niet
meteen duidelijk is.
- Waneer we het hebben over cohesie van methodes proberen we de ideale situatie te
bereiken dat een methode verantwoordelijk is voor exact 1 goed omschreven taak.
- Voordelen van cohesie:
a) betere leesbaarheid
b) mogelijkheid tot hergebruik
- herschikken is de activiteit waarmee de structuur van bestaande klassen en
methodes worden aangepast om ze te kunnen gebruiken wanneer de toepassing
gewijzigd of uitgebreid worden.
1) eerst wordt de toepassing herschikt zodat deze dezelfde functionaliteit heeft als
de oorspronkelijke versie. Zodra we deze stap voltooid hebben, moeten we de
regressietest uitvoeren om te kijken of we geen onbedoelde fouten
geïntroduceerd hebben.
2) Daarna (alleen als zeker is dat de functionaliteit van de eerste versie =
functionaliteit van de tweede versie) verbeteren we het programma. Daarna
testen we opnieuw.
 een methode is te lang als deze meer dan 1 logische taak uitvoert
 een klasse is te complex als deze meer dan 1 logische entiteit vormt
-
TOEPASSINGEN ZONDER BLUEJ

-
klasse methodes ( static methodes )
kunnen aangeroepen worden zonder instantie, alleen de klasse volstaat
-

-
-
een klassemethode word gedefinieerd door het begrip static voor de typenaam in het
argument van de methode te plaatesen
bv.
public static int aantaldagen ( ) { … }
zo’n methode kan worden aangeroepen door de naam van de klasse waarin deze
gedefinieerd is te specifiëren voor het punt
bv.
int days = Calendar.aantaldagen () ;
de methode main
in eerste instantie hebben we alleen klassen, dus de eerste methode die we toe
kunnen passen moet een klassemethode zij: je specifiëert de klasse die gestart moet
worden en Java zal dan een methode in die klasse aanroepen met de naam main
beperkingen van klassemethodes:
1) een klassemethode mag geen instantievelden benaderen die gedefinieerd zijn in
de klasse
2) een klassemethode mag geen instantiemethode van de klasse aanroepen
bv.
public static void main ( String [] args )
{
Game game = new Game ();
Game.start ();
}
Deel 2 : De structuur van toepassingen
HFST 8
-
-
-
-
overerving is een mechanisme waarmee we ons probleem van de gedupliceerde
code kunnen oplossen.
Overerving is soms ook een ‘is een’-relatie genoemd, een subklasse is immers een
specialisatie van de superklasse.
Het is mogelijk om meer dan 2 subklassen te laten erven van dezelfde superklasse en
een subklasse kan op zijn beurt weer een superklasse zijn voor andere subklassen.
De klassen vormen dan een overervingshiërarchie.
Overerving in Java
 public class Subklasse extends Superklasse
 het begrip super is een aanroep van de constructor van de superklasse
in Java moet een constructor van een subklasse altijd de constructor van de
bijbehorende superklasse als eerste statement aanroep
als je deze aanroep van de constructor van de superklasse niet expliciet schrijft, zal
de Java-compiler zo’n aanroep automatisch proberen in te voegen om er voor te
zorgen dat de velden van de superklasse correct geïnitialiseerd worden. Het
automatisch invoegen van deze aanroep zal alleen lukken als de superklasse een
constructor zonder parameters heeft.
voordelen van overerving
1) voorkomen van gedupliceerde code
2) hergebruik van code
3) gemakkelijk onderhoud
4) uitbreidbaarheid
substitutie  we kunnen een subklasse object gebruiken waar een superklasse
object verwacht worden, omdat het subklasse object een speciale uitvoering van de
superklasse is. Maar omgekeerd is dit niet toegestaan.
-
-
Hoewel we een expliciete superklasse kunnen declareren, even alle klassen waarvoor
geen superklasse gedeclareerd is, altijd impliciet van een klasse met de naam Object
Reden voor een superklasse voor alle objecten:
1) de klasse Object kan methodes definiëren die dan automatisch beschikbaar zijn
voor elk bestaand object
2) we kunnen polymorfe variabelen declareren van het type object die een
willekeurig object kunnen bevatten
Java-collecties  polymorfe collecties (ze kunnen gelijktijdig verschillende soorten
constructies bevatten)
We kennen in Java primitieve types (int, boolean, char …) die geen objecttypes zijn.
De oplossing zijn wrapperklassen: elk primitieve type in Java heeft een
bijbehorende wrapperklasse die hetzelfde type representeert, maar een echt
objecttype is
Bv.
voor int  Integer
HFST 9
-
-
-
een subklasse erft de informatie van de superklasse, maar deze heeft geen kennis
van de subklasse
we noemen het gedeclareerd type van de variabele het statische type, omdat het
gedeclareerd is in de broncode
we noemen het type van het object dat opgeslagen is in een variabele het
dynamische type, omdat het afhankelijk is van de toekenningen tijdens de
uitvoering van het programma
in tegenstelling tot de super-aanroepen in constructors moet de naam van de
methode van de superklasse expliciet geschreven worden
super.methodenaam ( eventule parameter );
in tegenstelling tot de super-aanroepen in de constructor moet de aanroep niet het
eerste statement zijn
in tegenstelling tot de super-aanroepen in de constructor wordt een super-aanroep in
methodes niet automatisch gegenereerd en zijn deze optioneel
de methode toString () wordt gebruikt om van een object een stringrepresentatie te
maken.
HFST 10
-
-
-
-
je herkent een abstracte methode aan
1) de definitie van de methode wordt voorafgegaan door abstract
2) de definitie van de methode heeft geen body (de header wordt afgesloten met
een puntkomma)
van abstracte klassen kunnen geen instanties worden gemaakt
alleen abstracte klassen kunnen abstracte methodes hebben; daardoor kunnen alle
methodes in concrete klassen altijd uitgevoerd worden. Als we een abstracte
methode in een concrete klasse zouden toestaan, zouden we een instantie van een
klasse kunnen maken zonder een implimentatie voor een methode
abstracte klassen met abstracte methodes zorgen ervoor dat subklassen de abstracte
methodes overschrijven en implimenteren. Als een subklasse geen implimentatie voor
een geërfde abstracte methode heeft, is deze zelf abstract en kunnen er geen
instanties gemaakt worden. Een subklasse is alleen concreet als deze implimentatiees
bevat voor alle geërfde abstracte methodes
doel van abstracte methodes = hoewel ze geen implimentatie leveren, garanderen
ze toch dat alle subklassen een implimentatie van deze methode hebben
-
-
-
-
meervoudige overerving bestaat in gevallen waarbij 1 klasse van meer dan 1
superklasse erft. De subklasse heeft dan alle kenmerken van de superklassen plus de
kenmerken die gedefinieerd zijn in de subklasse zelf
in Java is meervoudige overerving niet toegestaan, maar men heeft wel interfaces,
waarmee een zekere vorm van meervoudige overerving mogelijk is:
 de methodes van interfaces hebben geen body ( ze lijken dus erg op abstracte
klassen waarin alle methodes abstract zijn)
 public interface Naam { … }
 alle methodes in een interface zijn abstract;; de methodes mogen geen body
hebben, het begrip abstract is NIET nodig
 de zichtbaarheid moet niet gedeclareerd worden (= het begrip public moet niet
voor elke methode gedeclareerd worden)
 een interface mag alleen velden hebben als waarde van een constante; de
begrippen public, static en final mogen weggelaten worden.
 een klasse kan van een interface op dezelfde manier erven als van een andere
klasse, Java gebruikt het begrip implements voor overervende intercaces
 als een klasse zowel een klasse uibreidt als een interface implimenteert, dan moet
het begrip extends als eerste in de header worden ingeschreven
 het aantal interfaces dat geïmplimenteerd kan worden, is onbeperkt
public class Naam implements nr1, nr2 { … }
wanneer de klasse bedoeld is om implimentaties voor een paar methodes te
bevatten, moeten we een abstracte klasse gebruiken; als we kunnen kiezen, genieten
interfaces onze voorkeur
als we een type als een abstracte klasse definiëren, kunnen subklassen geen andere
klasse uitbreiden. Omdat interfaces meervoudige overerving mogelijk maken, kunnen
we deze daar dus wel voor gebruiken
HFST 11
-
-
-
-
een exception opgooien:
throw new Exeption type ( “Uitleg” );
alle subklassen van de Java-standaardklasse RunTimeExeption zijn ongecontroleerde
exeptions; alle andere subklassen van Exeption zijn gecontroleerde exeptions
Gecontroleerd
=
waar je een fout kan verwachten
Ongecontroleerd
=
normaal geen fouten ( programmeerfouten)
Gebruik ongecontroleerde exeptions voor situaties die leiden tot een
programmeerfout (meestal omdat te verwachten is dat een logische fout het
programma zal doen stoppen)
Een gecontroleerde exeptions gebruik je dus best voor situaties waar er een
probleem ontstaat dat mogelijk door de gebruiken kan worden opgelost
Ongecontroleerde exeptions gebruik je voor situaties die redelijkerwijs hadden
voorkomen kunnen worden. Het is dan ook duidelijk dat gecontroleerde exeptions
gebruikt moeten worden in situaties waarover de programmeur geen macht heeft
Wanneer een exeption wordt opgegooid, wordt de uitvoering van de opgooiende
methode onmiddellijk stopgezet  een specifiek gevolg daarvan is dat een methode
met een returntype anders dan void verplicht is om returnstatements uit te voeren op
een pad dat een exeption opgooit  als de exeption niet opgevangen wordt zal het
programma beëindigd worden met een foutmelding
Voor het opgooien van een ongecontroleerde exeption gebruik je een throwstatement
Bv.
throw new IllegalArgumentexeption (“Foute”);
-
-
-
-
-
-
-
 opgegooid door een constructor of methode om aan te geven dat de waarde van
een of meerdere argumenten ongeldig zijn
meestal is er een exeption-handler in de vorm van een try-blok:
try
{
// benadert 1 of meerdere statements
}
catch ( Exeptiontype e )
{
// meldt een exeption en lost het op
}
catch ( Exeptiontype e)
{
// tweede probleemsoort
}
het doorgeven van exeptions wordt veel gebruikt wanneer de aanroepende methode
zelf geen oplossing kan bieden voor de exeption; naar methodes die dit wel kunnen
op een hiërarchisch hoger niveau
de try-blok kan nog een derde component bevatten: de finally-clausule ( gebruikt
voor statements die altijd, ongeacht de aanwezigheid van een exeption, uitgevoerd
worden na een bewaakt statement)
 een finally-clausule wordt ook uitgevoerd als er een returnstatement uitgevoerd
wordt in de try- of catch-clausule
 als er een exeption opgegooid wordt in de try- of catch-clausule die niet
opgevangen wordt, wordt de finally-clausule toch uitgevoerd
als de standaard exeptionklasse niet passend genoeg is voor een bepaald probleem,
kan je zelf nieuwe meer specifieke exeptionklassen definiëren en daarbij overerving
gebruiken
meestal zal de belangrijkste reden voor het nieuw definiëren van een exeptionklasse
zijn om de aanvullende informatie op te nemen in het exeption-object om een betere
foutdiagnose en oplossing van de fout mogelijk te maken
fouten oplossen:
 een eerste vereiste voor het met succes oplossen van fouten is dat de gebruiker
iets doet met de ontvangen foutmelding. Het try-blok van Java is de manier om een
foutenoplossingsmechanisme te leveren bij een opgegooide exeption. De oplossing
van een fout zal meestal bestaan uit 1 of meerdere handelingen binnen de catchclausule gevolgd door een nieuwe poging. Om pogingen te herhalen kan het try-blok
in een lus geplaatst worden
fouten voorkomen:
om fouten te voorkomen is vaak een nauwe samenwerking nodig tussen
programmeur en gebruiker.
Download