Forrige kapittel Neste kapittel

Kapittel 3 — Introduksjon til klasser og objekter

Å lære noe er den største av alle gleder, ikke bare for filosofer, men også for resten av menneskene, hvor lite deres evne til det enn er.
Aristoteles

Innledning

I kapittel 1 ble java karakterisert som et objektorientert programmeringsspråk. Det ble skrevet at java kan brukes til å modellere virkeligheten ved å beskrive dens objekter i form av klasser. Det ble gitt en generell beskrivelse av hvordan en klassedefinisjon ser ut. I kapittel 2 tok vi for oss noen grunnleggende programelementer og brukte dem i noen enkle programeksempler. Alle programmene skrev vi i form av en klasse. Men klassen ble bare brukt som en ramme eller innpakning til programmet, den hadde ikke som oppgave å gi en modell av noen type objekt. Alle applikasjonsprogrammer må ha en slik "innpakning" som vi brukte i programeksemplene i kapittel 2: En klasse som inneholder en main-metode. Men det vi vanligvis bruker klasser til i et javaprogram, og som vi kommer til å gjøre heretter, er nettopp til å modellere objekter. I dette kapitlet blir det gitt noen innledende eksempler på dette.

Merknad: Bruk av pseudo-kode

Når vi skal skrive et program, vil det ofte være en god hjelp å starte med å lage en skisse av programmet for hånd. Etter hvert som vi får det klarere for oss både hva programmet skal utføre og hva vi må skrive av java-kode for å oppnå det vi ønsker, kan mer og mer av skissen erstattes av java-kode. Når vi er kommet så langt at hele skissen er erstattet av java-kode, kan vi prøve å kompilere og eventuelt kjøre den koden vi har skrevet.

For å skrive skisser, bruker vi det som kalles pseudo-kode. Med pseudo-kode så menes en blanding av java-kode og vanlig språk. Pseudo-kode kan ikke utføres på noen datamaskin, men har bare som hensikt å være til hjelp i utviklingen av et program. For å markere klart hva som er pseudo-kode til forskjell fra rein java-kode, skal vi (vanligvis) bruke en egen skriftfarge for pseudokode. Der det ellers ikke går klart fram av sammenhengen hva som er rein java-kode og hva som er pseudo-kode, skal vi plassere pseudo-kode mellom spissparenteser: < her plasserer vi pseudo-kode >. Spissparentesene er da ikke en del av selve pseudo-koden. De brukes bare til å markere hvor pseudo-koden begynner og slutter.

I kapittel 1 ble den generelle strukturen til en klasse beskrevet. Når vi bruker pseudo-kode, kan vi beskrive strukturen til en klasse på følgende måte:

public class Klassenavn
{
  < datafelter (variable og konstanter) > // for lagring av objektenes data
  < metoder (operasjoner som skal kunne utføres på objektene som
                          klassen definerer) >
}

Vi skal nå bruke denne strukturen på konkrete eksempler.

I læreboka til Deitel & Deitel, 9. utgave, brukes som eksempel en klasse som er kalt GradeBook. Klassen skal representere en slags karakterbok som lærere kan bruke til å holde oversikt over studentenes prestasjoner i et kurs som de underviser. Vi skal ta for oss eksempler som er en fornorsket utgave av "karakterbok-klassen" til Deitel & Deitel. Vi skal kalle klassen for Kursbok. Denne blir først definert i en svært enkel versjon, og så blir den etter hvert redefinert i nye versjoner som blir utstyrt med mer og mer funksjonalitet. I Deitel & Deitels lærebok har alle de forskjellige versjonene av klassen samme navn: GradeBook. Vi skal, for å skille dem fra hverandre, nummerere versjonene med Kursbok1, Kursbok2, Kursbok3 etc. I tillegg til den norske tilpasningen av navn og annet innhold, skal vi endre programmene slik at brukerkommunikasjonen blir foretatt ved hjelp av dialogbokser, tilsvarende som det er gjort i programeksemplene i kapittel 2. Slik programmene er skrevet i Deitel & Deitel, vil all brukerkommunikasjon foregå via konsollvinduet.

Eksempel 1: Definere en klasse som inneholder en metode og opprette et objekt av klassen

Vi skal altså definere en klasse som gir en modell av en kursbok. Av den generelle klassemodellen ovenfor ser vi at vi må definere en klasse som vi i første omgang kan skissere slik (ved bruk av pseudo-kode):

public class Kursbok1
{
  < Variable for eventuelle data som skal registreres i kursboka >
  < Definisjon av operasjoner som skal kunne utføres på kursboka >
}

I vår første, enkle modell for kursboka skal vi, for enkelhets skyld, ikke kreve at det kan foretas noe registrering av data. Klassen trenger derfor ikke å inneholde noen variable til dette bruk. Den eneste operasjon som skal kunne utføres på kursboka er å vise dens tittel på skjermen. Som tittel kan vi kort og godt bruke "Kursbok". Operasjoner definerer vi i form av det som i java kalles metoder. En metode kan vi tenke på som en navngitt samling av instruksjoner som til sammen skal utføre en bestemt oppgave. I vårt tilfelle trenger vi derfor å definere en metode som inneholder en instruksjon for å vise kursbokas tittel på skjermen. For å kunne definere denne metoden, trenger vi først å vite litt om reglene for å definere metoder. Vi må derfor se litt på disse reglene før vi kan fortsette med eksemplet vårt.

Det er viktig å ha klart for seg skillet mellom det å definere en metode og det å gjøre kall på den (kalle den opp). I definisjonen av en metode skriver vi de instruksjonene som skal utføres når metoden blir kalt opp. Disse instruksjonene blir imidlertid bare utført når metoden blir kalt opp. De blir utført hver gang metoden blir kalt opp, men ellers ikke. Reglene for å gjøre kall på metoder tar vi for oss etter at vi først har sett nærmere på hvordan metoder defineres.

Definere en metode

I en metodedefinisjon er det enkelte elementer som det er frivillig å ta med. Men alltid må en metodedefinisjon inneholde følgende deler, uttrykt i pseudokode:

  returverditype metodenavn( parameterliste )
  {
    < instruksjon(er) som skal utføres når metoden blir kalt opp >
  }

Metodenavnet og parameterlista utgjør det som blir kalt metodens signatur. Det er den som identifiserer hvilken metode det dreier seg om. (For å identifisere metoden bruker java dessuten navnet på den klassen som inneholder metodedefinisjonen.) Returverditypen angir datatypen for eventuell verdi som metoden skal returnere når den blir kalt opp. Returverdien kan være av en hvilken som helst datatype. Dersom metoden ikke skal returnere noe, skriver vi nøkkelordet void istedenfor datatype for returverdi. Metodens parameterliste kan være tom eller bestå av en eller flere parametre. Parametrene er variable som brukes inni metoden. For hver parameter må vi angi datatype og navn, med komma mellom hvert slikt par. Parametrene brukes i hovedsak til å gi metoden input når den blir kalt opp, det vil si å 'mate den med verdier'. For metoder som ikke trenger noen input, er parameterlista tom, det vil si metoden har ingen parametre. Men parentesene som omslutter parameterlista er det alltid nødvendig å ta med. Det er de som forteller at det navnet som står foran dem er navnet på en metode.

For øvrig gjelder i java den regel at alle metodedefinisjoner må plasseres i en eller annen klasse.

Vi går nå tilbake til vår klasse Kursbok1 som er skissert ovenfor. Vi fant ut at vi trengte å definere en metode som når den blir kalt opp viser kursbokas innhold på skjermen. En instruksjon for dette må derfor stå inni metoden. Og fra eksemplene i kapittel 2 vet vi hvordan en slik instruksjon kan se ut. Metoden vår skal ikke utføre noen andre oppgaver og trenger derfor ikke å inneholde noen andre instruksjoner. Når den blir kalt opp, trenger den ikke å returnere noen verdi. Som returverditype kan vi derfor bruke nøkkelordet void. Vi må finne på et navn på metoden vår. Generelt bør vi i programmene våre bruke det vi kan kalle selvforklarende navn, det vil si navn som indikerer hvilken rolle det som blir navngitt skal ha i programmet. I dette tilfellet har metoden som oppgave å vise kursbokas tittel. Et passende navn på metoden kan derfor være visTittel. Det er ikke nødvendig å gi metoden noe input, det vil si tilføre den noe data for at den skal kunne utføre oppgaven sin, for vi har allerede bestemt oss for at den skal skrive ut "Kursbok" som tittel på kursboka. Metoden trenger derfor ingen parametre. Alt i alt kan vi derfor skrive klassen vår Kursbok1 på følgende måte, med definisjon av metoden visTittel som eneste innhold:

public class Kursbok1
{
  public void visTittel()
  {
    JOptionPane.showMessageDialog( null, "Kursbok" );
  }
}

I definisjonen av metoden visTittel er det, i tillegg til det som er nevnt ovenfor, lagt inn nøkkelordet public. En fullgod forklaring på virkningen av dette har du ikke mulighet til å forstå før du har lært mer java. Foreløpig kan du oppfatte det som en indikasjon på at det skal være tillatt for alle å gjøre kall på metoden visTittel.

Klassen Kursbok1 er i seg selv ikke noe fullstendig program, og er heller ikke ment å være det. Klassen har som oppgave å definere en bestemt type objekt. Skal vi ha noen nytte av slike objektdefinisjoner, må vi opprette objekter av den definerte typen og bruke dem i programmer. Programmer kan vi lage slik vi lærte det i kapittel 2. Vi skal nå skrive et program som oppretter et objekt av type Kursbok1 og gjør kall på objektets metode visTittel. En skisse for programmet kan vi skrive slik, uttrykt i pseudo-kode:

public class Kursboktest1
{
  public static void main( String[] args )
  {
    < Opprett et Kursbok1-objekt >

    < Gjør kall på objektets visTittel-metode >
  }
}

For å kunne erstatte pseudo-koden med java-kode, trenger vi å vite hvordan vi oppretter objekter og gjør kall på deres metoder. Nedenfor er det forklart hva du trenger å vite om dette i denne omgang.

Hvordan opprette objekter

For å opprette objekter bruker vi nøkkelordet new sammen med navnet på klassen som definerer den type objekt som vi vil opprette, samt et parentespar av vanlig type. (Vi skal seinere se at det mellom parentesene kan være aktuelt å plassere en eller flere dataverdier.) For å opprette et Kursbok1-objekt skriver vi derfor

  new Kursbok1()

For å kunne ha noen nytte av slike objekter, trenger vi vanligvis etterpå å kunne referere til dem. Det kan vi få til ved bruk av variable. Fra kapittel 2 husker vi at når vi deklarerer en variabel, så må vi angi datatypen for verdiene som vi skal kunne tilordne variabelen. Spørsmålet nå blir derfor hva som er datatypen til et Kursbok1-objekt. Her er det faktisk slik at når vi skriver en klasse, slik vi har gjort med Kursbok1, så definerer vi samtidig en ny datatype. Når vi skal referere til et Kursbok1-objekt, trenger vi derfor en variabel av type Kursbok1! Det vanlige er at samtidig som vi oppretter et objekt ved å bruke nøkkelordet new, så bruker vi objektet til å initialisere en variabel av den type som objektet tilhører. I vårt tilfelle er det derfor aktuelt å skrive slik:

  Kursbok1 bok = new Kursbok1();

Som navn på objektet, eller rettere sagt den variabel som refererer til objektet, er her brukt bok. Vi kunne selvsagt like godt brukt et annet navn. Vi bør imidlertid, som nevnt ovenfor, alltid følge regelen om å bruke selvforklarende navn.

Hvordan gjøre kall på metoder

Som nevnt ovenfor, så gjør vi kall på en metode hver gang vi ønsker å få utført de instruksjonene som vedkommende metode inneholder. Hva som må skrives av kode for å gjøre et metodekall, avhenger både av hvordan metoden er definert og hvor kallet skjer. Vi skal ta for oss de forskjellige situasjonene etter hvert som vi får bruk for dem. For øyeblikket har vi følgende situasjon:
Vi skal gjøre kall på metoden visTittel, definert i klassen Kursbok1, og kallet skal skje utenfor denne klassen.
For å gjøre kallet, trenger vi da et objekt av type Kursbok1. Når vi har opprettet objektet bok, slik det er gjort ovenfor, kan vi skrive kallet slik:

  bok.visTittel();

Denne instruksjonen kan vi lese som "bok sin visTittel". Når vi bruker et objekt til å gjøre kall på en av objektets metoder, slik vi har gjort her, brukes også uttrykksmåten at vi "sender en melding" til objektet. Vi kan tenke på det som at vi gir objektet ordre om å utføre en handling, nærmere bestemt utføre instruksjonene i den metoden som vi spesifiserer.

Vi vet nå hvordan vi skal erstatte pseudo-koden i skissen ovenfor med rein java-kode og kan derfor skrive programmet vårt Kursboktest1 på følgende måte:

public class Kursboktest1
{
  public static void main( String[] args )
  {
    // Oppretter et Kursbok1-objekt:
    Kursbok1 bok = new Kursbok1();

    // Gjør kall på objektets visTittel-metode:
    bok.visTittel();
  }
}

Oppgave 1

Skriv inn klassene Kursbok1 og Kursboktest1 i TextPad. I java-fila for Kursbok1 må du importere klassen JOptionPane, slik det er gjort i programeksemplene i kapittel 2, siden den brukes av metoden visTittel. Dersom du ikke vil skrive filene selv (det kan for treningens skyld være lurt å gjøre det), kan du laste dem ned ved å klikke på følgende linker: Kursbok1.java, Kursboktest1.java. For å kjøre programmet, må du kjøre fila Kursboktest1.java, siden det er den som inneholder programmets main-metode. Når du kjører det, skal du få fram følgende meldingsboks på skjermen:

Eksempel 2: Definere og kalle opp en metode med parameter

I programeksemplet ovenfor opprettet vi ett eneste objekt av type Kursbok1. Men det er ikke noe i veien for at vi kan opprette flere objekter av denne typen, faktisk så mange objekter som vi bare vil. Hver gang vi bruker nøkkelordet new slik vi har gjort ovenfor, blir det opprettet et nytt objekt. Vi tenker oss nå at en lærer underviser to forskjellige kurs, og at læreren ønsker å opprette en kursbok for hvert kurs. For å kunne holde kursbøkene fra hverandre, er det da ønskelig at den tittel som skrives ut ikke bare består av navnet "Kursbok", men i tillegg inneholder navnet på det kurset som det er kursbok for. Metoden visTittel trenger da å vite hvilket kursnavn den skal skrive ut. Dessuten må den kunne skrive ut forskjellig kursnavn, avhengig av hvilket kurs vedkommende kursbokobjekt er kursbok for. Vi har derfor behov for å informere visTittel-metoden om hvilket kursnavn den skal skrive ut når den blir kalt opp. Informasjon til metoder kan vi få gitt ved å bruke parametre. I den generelle beskrivelsen av en metodedefinisjon som ble gitt ovenfor, står det nettopp at det skal være en parameterliste. Når vi ikke har behov for parametre, er parameterlista tom. Det var tilfelle for vår visTittel-metode definert ovenfor. Nå skal vi definere en ny versjon av denne metoden. Den skal ha en parameter som inneholder navnet på det kurset som klassen vår definerer kursbok for. Når vi endrer egenskapene til en metode, endrer vi også egenskapene til den klassen som metoden ligger i. Det er derfor grunn til å gi klassen et nytt navn. (Vi skal seinere se at vi innenfor én og samme klasse kan definere flere versjoner av én og samme metode, der versjonene skiller seg fra hverandre ved at parameterlista er forskjellig, mens metodenavnet er felles. Men nå foreløpig definerer vi isteden en ny klasse.) Vi skal kalle den nye klassen Kursbok2 og kan definere den på følgende måte:

public class Kursbok2
{
  public void visTittel( String kursnavn )
  {
    JOptionPane.showMessageDialog( null, "Kursbok for " + kursnavn );
  }
}

Legg merke til at vi i utskriftsinstruksjonen refererer til metodeparameteren kursnavn. Husk at instruksjonene som står i en metode ikke blir utført uten at vi gjør kall på metoden. Vi skal nå lage et program som oppretter et objekt av type Kursbok2, leser inn kursnavn fra brukeren, og viser på skjermen hvilket kurs vi nå har opprettet kursbok for. Det vil da bli forklart hvordan vi gjør kall på vår nye visTittel-metode og hvordan metodens parameter virker.

Vi kan lage følgende skisse for det nye programmet vårt:

public class Kursboktest2
{
  public static void main( String[] args )
  {
    Kursbok2 bok = new Kursbok2();

    < Les inn kursnavn >

    < Vis hvilket kurs det er opprettet kursbok for >
  }
}

Innlesing av kursnavn kan vi gjøre på tilsvarende måte som vi gjorde i programeksempel 3 i kapittel 2, ved at vi skriver

    String navn = JOptionPane.showInputDialog( "Skriv kursnavn:" );

Vi ønsker nå å gjøre kall på visTittel-metoden til Kursbok2-objektet bok på en slik måte at metoden bruker det innleste navnet i sin utskrift av kurstittel. Dette får vi til ved at vi skriver følgende metodekall:

    bok.visTittel( navn );

Vi ser at navn her er den String-variabel som vi brukte for å lagre det innleste kursnavnet. Når vi bruker variabelen som parameter i kallet på metoden visTittel, slik det er gjort ovenfor, sier vi at navn er aktuell parameter i metodekallet. Definisjonen av metoden visTittel, som står ovenfor, så ut slik:

  public void visTittel( String kursnavn )
  {
    JOptionPane.showMessageDialog( null, "Kursbok for " + kursnavn );
  }

I denne definisjonen kaller vi kursnavn for en formell parameter. Når vi gjør kall på metoden, må vi istedenfor den formelle parameteren bruke en aktuell parameter av samme datatype som den formelle. (Men legg merke til at i kallet spesifiserer vi ikke datatypen for den aktuelle parameteren, slik vi gjør for den formelle parameteren i metodedefinisjonen.) Den aktuelle parameteren kan enten være en konkret dataverdi av vedkommende datatype, eller det kan være, slik som her, en variabel av vedkommende datatype. Denne variabelen må da på forhånd ha blitt tilordnet en verdi (av riktig datatype). I begge tilfeller virker kallet på følgende måte:
Verdien til den aktuelle parameter blir tilordnet som verdi til den variabel som er formell parameter. Denne verdien brukes når metoden utfører sine instruksjoner. Resultatet blir derfor at vi via parameteren får tilført metoden en verdi utenfra. Den tilførte verdien kan selvsagt være forskjellig fra gang til gang som vi gjør kall på metoden.

Når det gjelder navnene til den formelle parameter og den variabel som eventuelt brukes som aktuell parameter, så er disse uavhengige av hverandre. I eksemplet ovenfor er det brukt forskjellige navn (kursnavn og navn). Men det er også tillatt å bruke samme navn. Det avgjørende er at den aktuelle parameter er av samme datatype som den formelle parameter.

På grunnlag av det vi nå vet, kan vi erstatte programskissen ovenfor med følgende java-kode:

public class Kursboktest2
{
  public static void main( String[] args )
  {
    Kursbok2 bok = new Kursbok2();

    // Leser inn kursnavn:
    String navn = JOptionPane.showInputDialog( "Skriv kursnavn:" );

    // Viser hvilket kurs vi har opprettet kursbok for:
    bok.visTittel( navn );
  }
}

I vedkommende java-fil må vi dessuten importere klassen JOptionPane. De to bildene nedenfor viser et eksempel på de dialogboksene vi får når vi kjører programmet og bruker Programmering som kursnavn.

Oppgave 2

Legg inn klassene Kursbok2 og Kursboktest2 i TextPad. Du kan få lastet ned filer med de to klassene ved å klikke på følgende linker: Kursbok2.java, Kursboktest2.java. Kjør programmet gjentatte ganger med innlesing av forskjellige kursnavn.

Eksempel 3: Objekter som inneholder data — set- og get-metoder

De kursbokobjektene vi har definert og opprettet hittil kan ikke være av særlig stor nytte, for det er ikke mulig å lagre noen data i dem. Dette skal vi rette på nå. I første omgang skal vi ta sikte på å lagre selve kursnavnet. Hva som kreves for at objekter skal kunne lagre data er allerede antydet i den skisse som først ble skrevet for kursbokklassen i eksempel 1 ovenfor. De variable som en klasse har for lagring av objektenes data, kan vi kalle klassens datafelter. Det er også vanlig å omtale disse som klassens attributter, mens metodene kalles klassens operasjoner. Som betegnelse for datafeltene brukes også uttrykkene objektvariable og instansvariable, siden et objekt også blir kalt for en instans av vedkommende klasse.

Av nevnte skisse ser vi at vi i vårt tilfelle må utstyre klassen med en variabel som kan inneholde kursnavnet, det vil si en variabel av type String. Vi skal også gjøre noen andre endringer. Hensikten med disse blir forklart nedenfor. Den nye versjonen av kursbokklassen ser ut som følger:

public class Kursbok3
{
  private String kursnavn;

  public void setKursnavn( String navn )
  {
    kursnavn = navn;
  }

  public String getKursnavn()
  {
    return kursnavn;
  }

  public void visTittel()
  {
    JOptionPane.showMessageDialog( null, "Kursbok for " + kursnavn );
  }
}

Variabelen kursnavn skal altså brukes til å lagre navnet på kurset som klassen definerer kursbok for. Vi legger merke til at det forrest i deklarasjonen av denne variabelen står nøkkelordet private. Dette har som virkning at variabelen bare vil være tilgjengelig inni klassen Kursbok3, det vil si at det bare er der det er mulig å referere til den slik at vi kan tilordne den en verdi, eller se hvilken verdi den har. Vi skal imidlertid, som i eksempel 2 ovenfor, bruke et kursbokobjekt i et program som leser inn kursnavn. Vi vil da være utenfor klassen Kursbok3. Spørsmålet blir dermed hvordan vi skal få overført det innleste navnet til objektet vårt. Det kan vi få gjort ved å gjøre kall på den nye metoden setKursnavn, med det innleste navnet som aktuell parameter. For den metoden overfører til datafeltet kursnavn den verdi som dens parameter navn har når metoden blir kalt opp. Dersom vi har et kursbokobjekt og vil se på hvilket kursnavn det inneholder, kan vi gjøre kall på dets metode getKursnavn. For den vil returnere verdien til datafeltet kursnavn til det stedet der metoden blir kalt opp. For å returnere en verdi fra en metode, bruker vi nøkkelordet return. Det har som virkning å avslutte hele metoden, samtidig som verdien av uttrykket bak return blir returnert til det stedet der metoden blir kalt opp. Datatypen for dette uttrykket må angis som returverditype foran metodenavnet, i dette tilfellet er det datatypen String.

Merknad 1

Det er vanlig praksis å gi klassenes datafelter såkalt private aksess, slik vi har gjort ovenfor. Hvorfor dette er viktig vil du få mer klart for deg etter hvert som vi tar for oss eksempler som er bedre egnet til å begrunne det. Alltid når vi har datafelter som har private aksess, vil det være behov for å ha såkalte get-metoder, tilsvarende som vi har for kursnavn, for å kunne lese av deres verdier utenfor klassen. Og dersom det er behov for å endre deres verdier når vi er utenfor klassen, så behøves tilsvarende set-metoder som vi har for kursnavn. En set-metode må ha en parameter av samme datatype som det datafelt den skal gi verdi til. En get-metode derimot, trenger ingen parameter.

Merknad 2

Du kan legge merke til at visTittel-metoden er endret i den nye versjonen av kursbokklassen. Kursnavnet blir nå lagret i et datafelt i klassen. Metoden kan derfor hente navnet derfra når den skal skrive ut tittel, den trenger ikke å motta navnet som parameter, slik det ble gjort i forrige versjon av klassen. Vi kunne også ha definert metoden visTittel på følgende måte:

  public void visTittel()
  {
    JOptionPane.showMessageDialog( null, "Kursbok for " + getKursnavn() );
  }

Her gjøres det kall på metoden getKursnavn for å hente kursnavnet. Men siden metoden visTittel blir definert inni klassen Kursbok3, kan vi referere direkte til alle klassens datafelter for å få tak i deres verdier, uavhengig av om de har private aksess eller ikke. Det vil være mer effektivt enn å gjøre kall på get-metoder for å hente deres verdier. Legg for øvrig merke til at når vi gjør kall på en metode innenfor samme klasse som metoden er definert, trenger vi ikke noe objekt etterfulgt av punktum for å gjøre kall på metoden.

***

Vi skal nå teste ut det nye kursbokobjektet i følgende program.

 1 import javax.swing.JOptionPane;
 2
 3 public class Kursboktest3
 4 {
 5   public static void main( String[] args )
 6   {
 7     Kursbok3 bok = new Kursbok3(); // oppretter objekt
 8
 9     // Viser opprinnelig verdi til datafeltet kursnavn:
10     JOptionPane.showMessageDialog( null, "Opprinnelig kursnavn: " +
11                                    bok.getKursnavn() );
12
13     // Leser inn kursnavn:
14     String navn = JOptionPane.showInputDialog( "Skriv kursnavn:" );
15     bok.setKursnavn( navn ); // lagrer innlest navn i objektet
16
17     // Viser den nye verdien til datafeltet kursnavn:
18     JOptionPane.showMessageDialog( null, "Registrert kursnavn: " +
19                                    bok.getKursnavn() );
20     // Skriver ut tittel med kursnavn:
21     bok.visTittel();
22   }
23 }

En kjøring av programmet med innlesing av Programmering som kursnavn gir følgende dialogbokser:


Dialogboksen viser resultatet av å hente ut verdien til datafeltet kursnavn før vi har gitt det noen verdi. Vi ser at det har verdien null.


Innlesing av Programmering som kursnavn.


Viser verdien som datafeltet kursnavn nå har fått.


Resultat av kall på metode visTittel.

Oppgave 3

Legg inn klassene Kursbok3 og Kurstest3 i TextPad. Du kan få lastet ned filer med de to klassene ved å klikke på følgende linker: Kursbok3.java, Kursboktest3.java. Kjør programmet gjentatte ganger med innlesing av forskjellige kursnavn.

Primitive datatyper kontra referansetyper (pekere)

Vi har sett at når vi tar i bruk en variabel i et program, så må vi spesifisere en datatype for variabelens verdier. Vi har brukt numeriske variable av datatypene int og double. I forbindelse med logiske uttrykk i kapittel 2 ble det nevnt at vi kan kan ha logiske variable, av datatype boolean. For tekst har vi brukt variable av datatype String. I programmene i dette kapitlet har vi brukt variable av de egendefinerte datatypene Kursbok1, Kursbok2 og Kursbok3. Det er en svært viktig forskjell mellom datatypene int, double, boolean og de andre som er nevnt. Datatypene int, double, boolean kalles primitive datatyper. En variabel av primitiv datatype kan bare lagre én enkelt dataverdi. Vi kan oppfatte vedkommende variabel som et navn på den lagrete verdien. (I tillegg til de nevnte, har vi i javaspråket de primitive datatypene byte, char, short, long og float. Disse kommer vi tilbake til etter hvert som vi får bruk for dem.) Alle andre datatyper som brukes i et javaprogram er definert i form av en klasse, tilsvarende som vi har gjort med datatypene Kursbok1, Kursbok2 og Kursbok3 i dette kapitlet. (String er også definert i form av en klasse. Den finnes i javas klassebibliotek, sammen med et stort antall andre typer som også er definert i form av klasser.) Slike datatyper kalles med et fellesnavn for referansetyper eller klassetyper. En variabel av en slik type kalles en referanse eller en peker. Grunnen til denne navnebruken er at klassene definerer objekter. Objekter oppretter vi ved bruk av nøkkelordet new, slik som vi har gjort for eksempel i instruksjonen

    Kursbok3 bok = new Kursbok3();

Virkningen av dette er at det i maskinens hurtiglager (memory) blir reservert plass til et nytt objekt av den spesifiserte typen, i dette tilfelle type Kursbok3. Nøkkelordet new er egentlig en operator. En operator er noe som returnerer en verdi. Den returnerte verdien fra new-operatoren er en referanse til det stedet i maskinens memory der det nyopprettede objektet ligger lagret. Denne referansen blir gitt som verdi til vedkommende variabel, i dette tilfelle variabelen bok. Vi sier derfor at denne refererer til eller peker på objektet, og selve variabelen kaller vi altså en referanse eller en peker. (Vi kan også tenke på referansen som selve memory-adressen til objektet.)

Objektet som blir opprettet kan i seg selv inneholde mange dataverdier, like mange som det er antall datafelter i klassen som definerer objektet. Pekeren til objektet vil ikke være navn på noen av disse dataverdiene, den bare refererer til objektet som inneholder dataverdiene.

Initialisering av objekters datafelter — konstruktører

Når vi oppretter et nytt objekt ved å bruke new-operatoren, vil alle datafeltene i klassen som definerer objektet automatisk bli tildelt startverdier. Dersom det i vedkommende klasse ikke er noen egne instruksjoner for tildeling av startverdi, blir datafeltene tildelt standardverdier. For numeriske variable er dette tallverdien 0 (eller 0.0, i tilfelle variabel for desimaltall). For logiske variable (datatype boolean) er det verdien false, og for alle referansevariable (med datatype definert i form av en klasse) er det verdien null, som også er et nøkkelord i java-språket. Verdien null indikerer at vedkommende peker ikke refererer til noe objekt. Ønsker vi å tildele datafeltene andre startverdier enn de nevnte standardverdiene, har vi flere muligheter for å gjøre det. En mulighet er å initialisere vedkommende datafelt samtidig som vi deklarerer det. I klassen Kursbok3 kunne vi for eksempel ha initialisert datafeltet kursnavn med verdien "foreløpig uten navn" ved at vi hadde skrevet klassedefinisjonen slik (uttrykt i pseudo-kode):

public class Kursbok3
{
  private String kursnavn = "foreløpig uten navn";

  < Resten av klassedefinisjonen som før >
}

En annen mulighet er å definere en eller flere såkalte konstruktører for å initialisere datafelter (det vil si tildele dem startverdier). En konstruktør er kjennetegnet ved at den har samme navn som klassen, samt en parameterliste mellom vanlige parenteser, tilsvarende som vi har i en metodedefinisjon. Som i en slik, kan parameterlista være tom. Men i motsetning til en metodedefinisjon, skal det ikke i en konstruktør være angivelse av noen returverditype, heller ikke void. Nøkkelordet public er imidlertid (vanligvis) nødvendig. Parametrene kan brukes til å overføre startverdier til datafelter, på tilsvarende måte som vi gjorde i Kursbok3-klassens setKursnavn-metode. I den nye versjonen Kursbok4 av kursbokklassen vår, som følger nedenfor i eksempel 4, er det en konstruktør som initialiserer kursnavnet på den nevnte måten. En konstruktør blir utført hver gang vi bruker new-operatoren, ellers ikke. Konstruktørens instruksjoner vil da bli utført, i tillegg til at det reserveres plass til det nye objektet som blir opprettet. Dersom konstruktøren har parametre, må vi, som i tilfelle metodekall, for hver formell parameter i konstruktørens definisjon ha en tilsvarende aktuell parameter av riktig datatype når vi bruker new-operatoren for å opprette et objekt.

En konstruktør uten parametre blir kalt for default-konstruktøren til vedkommende klasse. Dersom klassedefinisjonen ikke inneholder noen konstruktør-definisjon, vil java-systemet automatisk opprette en slik default-konstruktør når vi bruker new-operatoren for å opprette et objekt. Default-konstruktøren vil da initialisere datafeltene med standardverdier. Det var dette som skjedde i programmet Kursboktest3 ovenfor. Men vi har altså mulighet til selv å definere en default-konstruktør med annen virkemåte, eller definere en konstruktør med en eller flere parametre. (Som vi skal se seinere, kan vi for én og samme klasse definere flere forskjellige konstruktører, med forskjellige parameterlister, slik at det er mulig å velge mellom flere måter å initialisere objekter på.)

Eksempel 4: Bruk av konstruktør. Ja-nei-dialogboks

Klassen Kursbok4 er utstyrt med en konstruktør som initialiserer datafeltet kursnavn via en konstruktørparameter.

 1 import javax.swing.JOptionPane;
 2
 3 public class Kursbok4
 4 {
 5   private String kursnavn;
 6
 7   // Konstruktør som initialiserer datafeltet kursnavn.
 8   public Kursbok4( String n )
 9   {
10     kursnavn = n;
11   }
12
13   public void setKursnavn( String navn )
14   {
15     kursnavn = navn;
16   }
17
18   public String getKursnavn()
19   {
20     return kursnavn;
21   }
22
23   public void visTittel()
24   {
25     JOptionPane.showMessageDialog( null, "Kursbok for " + kursnavn );
26   }
27 }

Programmet Kursboktest4 som er gjengitt nedenfor deklarerer to objekter av type Kursbok4. Kursnavn for den første kursboka blir innlest fra brukeren. Det innleste navn blir via konstruktørparameter overført til det objekt som blir opprettet. Programmet spør deretter brukeren om han/hun ønsker å opprette en kursbok til. Til dette brukes instruksjonen

    int svar = JOptionPane.showOptionDialog( null,
        "Vil du opprette en kursbok til?",
        "Opprette flere kursbøker?", JOptionPane.YES_NO_OPTION,
        JOptionPane.QUESTION_MESSAGE, null, null, null );

Vi ser at det her står en tilordningsinstruksjon. Vi vet at det da er høyre side av denne som utføres først. Det er metodekallet

    JOptionPane.showOptionDialog( null,
	        "Vil du opprette en kursbok til?",
	        "Opprette flere kursbøker?", JOptionPane.YES_NO_OPTION,
                JOptionPane.QUESTION_MESSAGE, null, null, null );

Det resulterer i at følgende dialogboks vises på skjermen:

Det er her selvsagt meningen at brukeren skal klikke på Yes- eller No-knappen for å svare på spørsmålet som stilles. Det er også tillatt for brukeren å klikke på dialogboksens lukkeknapp. Klikk på en av de tre nevnte knapper vil resultere i at boksen lukker seg og til kallstedet vil det bli returnert en int-verdi som er en av følgende forhåndsdefinerte konstanter, avhengig av hvilken knapp det ble klikket på: JOptionPane.YES_OPTION, JOptionPane.NO_OPTION, JOptionPane.CLOSED_OPTION. Den returnerte verdien blir tilordnet som verdi til int-variabelen svar. Vi ønsker at programmet skal virke på den måten at dersom det ble klikket på Yes-knappen, så skal programmet opprette en kursbok til, ellers ikke. For å oppnå dette, skriver vi derfor instruksjonen

    if ( svar == JOptionPane.YES_OPTION )
    {
      // Leser inn kursnavn:
      navn = JOptionPane.showInputDialog("Skriv kursnavn:");
      bok2 = new Kursbok4(navn);
    }

Til slutt skriver programmet ut en melding om hvilke fag det er opprettet kursbøker for. Innholdet av fila Kursboktest4.java er gjengitt nedenfor.

 1 import javax.swing.JOptionPane;
 2
 3 public class Kursboktest4
 4 {
 5   public static void main( String[] args )
 6   {
 7     Kursbok4 bok1 = null, bok2 = null;
 8     // Leser inn et kursnavn:
 9     String navn = JOptionPane.showInputDialog( "Skriv kursnavn:" );
10
11     bok1 = new Kursbok4( navn ); // oppretter objekt med innlest kursnavn
12
13     int svar = JOptionPane.showOptionDialog( null,
14             "Vil du opprette en kursbok til?",
15             "Opprette flere kursbøker?", JOptionPane.YES_NO_OPTION,
16             JOptionPane.QUESTION_MESSAGE, null, null, null );
17
18     if ( svar == JOptionPane.YES_OPTION )
19     {
20       // Leser inn kursnavn:
21       navn = JOptionPane.showInputDialog("Skriv kursnavn:");
22       bok2 = new Kursbok4(navn);
23     }
24
25     // Viser kursnavn for opprettede kursbøker:
26     String info = "Har opprettet kursbøker for følgende fag:\n";
27     info = info + bok1.getKursnavn();
28     if ( bok2 != null )
29       info = info + "\n" + bok2.getKursnavn();
30
31     JOptionPane.showMessageDialog( null, info, "Registrert kursnavn",
32                                    JOptionPane.PLAIN_MESSAGE );
33   }
34 }

Nedenfor blir det vist dialogbokser fra en kjøring av programmet, der det ble klikket på Yes-knappen i Ja-nei-dialogboksen.

Oppgave 4

Legg inn klassene Kursbok4 og Kursboktest4 i TextPad. Du kan få lastet ned filer med de to klassene ved å klikke på følgende linker: Kursbok4.java, Kursboktest4.java. Kjør programmet gjentatte ganger med innlesing av forskjellige kursnavn og med vekslende bruk av de tre knappene i Ja-nei-dialogboksen.

Forrige kapittel Neste kapittel

Copyright © Kjetil Grønning og Eva Hadler Vihovde, revidert 2014