Indhold:
Klassevariable
Repetition af objektvariabler og lokale variabler
Rekursion
Forudsættes ikke i resten af bogen.
Forudsætter kapitel 4, Definition af klasser.
De variabler, vi er stødt på indtil nu, har enten været lokale variabler eller objektvariabler.
Objektvariabler hedder sådan, 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 static-nøgleordet. 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" objektvariabler, så hvert Boks-objekt har tilknyttet en 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 objektorienteret programmeret.
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, linieskift, 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 2.12.4, Klassemetoder.
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, // fordi 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.
Parametervariablerne 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 } System.out.println(); } .... int stj = 10; udskrivStjerner(stj); // kald af udskrivStjerner // stj er stadig 10. ...
Kalderen mærker intet til, at parametervariablen 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 metoden herunder godt ændre på kalderens punkt-objekt:
public void flyt(Point p, int dx, int dy) { p.x = p.x+dx; // OK, vi ændrer på kalderens objekt p.y = p.y+dy; } ... Point p1 = new Point(); p1.x = 10; p1.y = 10; flyt(p1,10,10); // nu er p1 (20,20) ...
Men man kan stadig ikke ændre på kalderens reference. Dvs. p1's værdi:
public void flyt(Point p, int dx, int dy) { // hmm... vi glemmer kalderens objekt, men det opdager han ikke p = new Point(p.x+dx, p.y+dy); } ... Point p1 = new Point(); p1.x = 10; p1.y = 10; flyt(p1,10,10); // nu er p1 stadig (10,10) ...
En lokal variabel oprettes, når man går ind i blokken, hvor den er defineret, og nedlægges igen, når man går ud af blokken. 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.