Indhold:
Klassevariable
Repetition af felter/objektvariabler og lokale variabler
Rekursion
Forudsættes ikke i resten af bogen.
Forudsætter kapitel Fejl: Henvisningskilde ikke fundet, Fejl: Henvisningskilde ikke fundet.
De variabler, vi er stødt på indtil nu, har enten været lokale variabler eller felter.
Felter kaldes også objektvariabler, fordi de bliver oprettet for hvert objekt.
Der findes også variabler, der eksisterer "i klassen", uafhængigt af om der bliver oprettet objekter. Disse kaldes klassevariabler og erklæres med nøgleordet static (derfor kaldes de ofte også for "statiske variabler").
Herunder ses et eksempel på en klassevariabel og en klassemetode (antalBokse).
Klassevariabler og -metoder vises med understregning i UML-notationen (diagrammet til højre).
public class Boks4
{
private double længde; // objektvariabel
private double bredde; // objektvariabel
private double højde; // objektvariabel
private static int antalBokse; // klassevariabel
public Boks4(double lgd, double b, double h)
{
// lgd, b og h er lokale variabler
længde = lgd;
bredde = b;
højde = h;
antalBokse = antalBokse + 1;
}
public static int læsAntalBokse() // klassemetode
{
return antalBokse;
}
public double volumen()
{
// vol er en lokal variabel
double vol;
vol = længde*bredde*højde;
return vol;
}
}
Variablen antalBokse er en klassevariabel, fordi den er erklæret med nøgleordet static. Dette betyder, at variablen er tilknyttet klassen og at alle Boks-objekter deler den samme variabel. Der vil eksistere én og kun én antalBokse-variabel, uanset om der oprettes 0, 1, 2 eller 100 Boks-objekter.
Ligeledes er metoden læsAntalBokse() en klassemetode, da den er erklæret med nøgleordet static. Den arbejder på klasseniveau (uafhængigt af om der er skabt nogen objekter) og kan derfor ikke anvende metoder og variabler i klassen, der eksisterer på objektnivau. Det er f.eks. forbudt at bruge variablerne længde, bredde eller højde inde fra læsAntalBokse().
Inde fra objektet kan klassevariabler (og -metoder) bruges ligesom almindelige variabler og -metoder. Det ses f.eks. i konstruktøren:
antalBokse = antalBokse + 1;
Og for fuldstændighedens skyld: Variablerne længde, bredde og højde er "normale" felter/objektvariabler, så hvert Boks-objekt har tilknyttet én af hver. Variablen vol er en lokal variabel, fordi den er erklæret lokalt i volumen-metoden og altså kun eksisterer, når volumen-metoden udføres. Ligeledes med lgd, b og h: De eksisterer kun i Boks' konstruktør.
Vi kan afprøve Boks4 med:
public class BenytBoks4
{
public static void main(String[] arg)
{
System.out.println("Antal bokse: "+ Boks4.læsAntalBokse());
Boks4 boksen;
boksen = new Boks4(2,5,10);
System.out.println("Antal bokse: "+ Boks4.læsAntalBokse());
Boks4 enAndenBoks, enTredjeBoks;
enAndenBoks = new Boks4(5,5,10);
enTredjeBoks = new Boks4(7,5,10);
System.out.println("Antal bokse: "+ Boks4.læsAntalBokse());
}
}
Antal bokse: 0
Antal bokse: 1
Antal bokse: 3
Det ses, at vi udefra kan kalde klassemetoder ved bare at angive klassenavnet og metodenavnet som i Boks4.læsAntalBokse(). Havde antalBokse været tilgængelig udefra (f.eks. erklæret public i stedet for private), kunne vi få fat i den udefra med: Boks4.antalBokse.
Vær opmærksom på, at det normalt frarådes at definere mange klassevariabler og -metoder i ens program, da det kan gøre programmet svært at gennemskue og øger de enkelte klassers afhængighed af hinanden (høj kobling). Endelig kan det friste folk til at springe "alt det besværlige med objekter" over og dermed ikke få lært eller udnyttet mulighederne i objektorienteret programmering.
Du har, måske uden at vide det, allerede brugt en del klassevariabler og -metoder.
Der er mange klassevariabler i Javas standardbibliotek. Af de oftest brugte kan nævnes
Matematiske konstanter som Math.PI (værdien af er klassevariabler i Math-klassen.
Foruddefinerede farver som Color.BLACK – et Color-objekt, der repræsenterer farven sort. Objektet ligger som en klassevariabel i (selvsamme) Color-klasse (pakken java.awt).
System.out – systemoutputtet er et PrintStream-objekt, der bl.a. har metoderne print() og println(). Objektet er en klassevariabel i System-klassen.
Klassevariabler er nyttige til variabler, der skal være tilgængelige overalt i programmet. Det er det nærmeste, man kommer globale variabler, som det kendes fra andre programmeringssprog.
Af nyttige klassemetoder i standardbiblioteket kan nævnes
Matematiske funktioner som Math.random(), Math.sin(double x), Math.cos(double x), Math.sqrt(double x), Math.abs(double x), Math.exp(double x), Math.log(double x), ...
Double.parseDouble(String s) returnerer værdien af s som et kommatal. Nyttig til at fortolke brugerindtastede tal. F.eks. giver Double.parseDouble("3.553") tallet 3.553.
Tilsvarende giver Integer.parseInt(String s) værdien af s som et heltal.
String.valueOf(double d) gør det modsatte af Double.parseDouble, den returnerer en streng, som repræsenterer et flydende kommatal. String.valueOf(3.21) giver altså strengen "3.21". Findes også med int, byte, char etc. som parameter.
Character.isDigit(character t) returnerer true eller false, afhængigt af om tegnet t er et ciffer. Ligeledes findes Character.isLetter(character t), Character.isLetterOrDigit(character t), Character.isLowerCase(character t), Character.isUpperCase(character t) og Character.isWhitespace(character t). Den sidste undersøger, om t er et usynligt skilletegn, f.eks. mellemrum, linjeskift, tabulator.
System.exit() – stopper programudførelsen og afslutter Java.
Det vigtigste eksempel på en klassemetode er main()-metoden, som du selv erklærer, når du skriver et program, f.eks. BenytBoks.main(). Når et program startes, er det altid main(), der kaldes. På dette tidspunkt eksisterer der endnu ingen objekter og derfor skal main() være en klassemetode. Der skal jo aldrig oprettes nogen BenytBoks-objekter!
Klassemetoder er nyttige til "subrutiner", der skal regne et eller andet ud (ud fra parametrene) og returnere resultatet. Et eksempel er vist i afsnit Fejl: Henvisningskilde ikke fundet, Fejl: Henvisningskilde ikke fundet.
Når en metode kaldes, opretter systemet en "omgivelse" for det metodekald. I denne omgivelse oprettes parametervariablerne og de lokale variabler.
En lokal variabel er kendt fra dens erklæring og ned til slutningen af den blok, der omslutter den
Dette kaldes variablens virkefelt
Den lidt indviklede formulering skyldes, at man kan lave variabler, der er lokale for en hvilken som helst blok – ikke kun en metode-krop. Man kan altså skrive noget som:
...
int a = 10;
while (a > 0)
{
double b; // b erklæres lokalt i while-blokken
b = Math.random();
...
System.out.println(b);
a--;
}
System.out.println(a);
System.out.println(b); // fejl: b eksisterer ikke, da vi er uden for blokken.
...
Vi har desuden allerede set, at man i for-løkker kan erklære en variabel, der er lokal for løkkens krop:
for (int i=0; i<10; i++)
System.out.print(i);
System.out.print(i); // fejl: i eksisterer ikke uden for løkken.
Parametervariabler får tildelt en kopi af den værdi, de blev kaldt med og opfører sig i øvrigt fuldstændigt som lokale variabler. Man kan f.eks. godt tildele dem nye værdier:
// metode, der udskriver et bestemt antal stjerner på skærmen.
public void udskrivStjerner(int antal)
{
while (antal>0)
{
System.out.print(”*”);
antal = antal-1; // Det kan man godt (men det er dårlig stil)
}
System.out.println();
}
....
int stj = 10;
udskrivStjerner(stj); // kald af udskrivStjerner
// stj er stadig 10.
Kalderen mærker intet til, at parametervariablen antal har fået en ny værdi, fordi kalderens værdi netop blev kopieret over i parametervariablen.
Her skal man være opmærksom på forskellen mellem variabler af simple typer og variabler af objekt-typer. Da det sidste er referencer (adresser på objekter), peger parametervariablen på samme objekt som kalderen, når den bliver kopieret. Ændrer man i objektet, slår ændringen også igennem hos kalderen.
Derfor kan flyt1() herunder godt ændre x og y i kalderens punkt-objekt:
import java.awt.Point;
public class Parametervariabler
{
public static void flyt1(Point p, int dx, int dy)
{
p.x = p.x+dx; // OK, vi ændrer på kalderens objekt
p.y = p.y+dy;
}
public static void flyt2(Point p, int dx, int dy)
{
// hmm... vi smider kalderens objekt væk... men det opdager han ikke!
p = new Point(p.x+dx, p.y+dy);
}
public static void main(String[] arg)
{
Point p1 = new Point(10,10);
flyt1(p1,13,14);
System.out.println("Nu er p1="+p1);
Point p2 = new Point(10,10);
flyt2(p2,13,14);
System.out.println("Nu er p2="+p2);
}
}
Nu er p1=java.awt.Point[x=23,y=24]
Nu er p2=java.awt.Point[x=10,y=10]
Vi kan ikke ændre på kalderens reference, som vi forsøger på i flyt2(): En lokal variabel oprettes, når man går ind i blokken, hvor den er defineret og nedlægges igen, når blokken forlades. Der bliver oprettet en ny variabel, hver gang programudførelsen går ind i blokken.
Når en metode kaldes, bliver der oprettet et nyt sæt lokale variabler (incl. parametervariabler) uafhængigt af, hvilke metoder, der i øvrigt kaldes eller er i gang med at blive kaldt
Hvis en metode bliver kaldt to gange, eksisterer der altså to versioner af den lokale variabel – én i hver deres omgivelse. Det behøver man som regel ikke at tænke på, men det er rart at have vished for, at en anden metode ikke bare kan ændre ens lokale variabler.
Rekursion er en teknik, der udnytter, at der bliver oprettet en ny omgivelse med nye lokale variabler, hver gang en metode kaldes. Nogle problemer kan løses meget elegant med rekursion. I Avanceret-afsnittet i slutningen af kapitlet vises nogle eksempler på rekursion.