Løsningsforslag ukeoppg. 8:  18. - 22. okt (INF1000 - Høst 2010)

Filer, toUpperCase, args[], private, static, og mer om objekter (kap. 8)
NB! Disse er bare forslag til løsninger, dine svar kan være like gode eller bedre selv om de ser annerledes ut.

Mål
Få mer øvelse i bruk av klasser og objekter, og lære litt mer om filer, store/små bokstaver, kommandolinjeargumenter, innkapsling, og klassevariabler og -metoder.

Oppgaver til teoritimen

  1. Spør læreren din:
    (a) Spør gruppelæreren din om et tema du ønsker litt mer forklaring på, f.eks. noe du finner vanskelig i Oblig 3.

    (b) Gå gjennom hint a‒h i Oblig 3.

    (c) Gjør ferdig Ukeoppgaver 6 og 7, særlig oppgave nr. 3 i Ukeoppgaver 7 om feilmeldinger.



  2. Utskrift av tabell: kap. 3, oppg. 2 (side 70)
    (a) Lag et program som benytter programpakken easyIO til å skrive ut følgende tabell på skjermen.  Tips: Se side 51 i læreboka.

    Bilmerke        Årsmodell       Reg.nr.
    ---------------------------------------
    Mercedes        1999            UE65660
    Ford            2003            ZE95523
    Toyota          2006            DK53401
    ---------------------------------------

    // Basert på løsningsforslaget i lærebokens hjemmeside:
    // http://www2.universitetsforlaget.no/java/eksempler.php
    import easyIO.*;
    
    class Biltabell {
        public static void main(String[] args) {
    	Out skjerm = new Out();
    	int bredde = 16;
    	String linje = "----------------------------------------";
    
    	// Overskrifter:
    	skjerm.out("Bilmerke", bredde);
    	skjerm.out("Årsmodell", bredde, Out.LEFT);
    	skjerm.outln("Reg.nr.");
    
    	skjerm.outln(linje);
    
    	// Innmat i tabellen, linje for linje:
    	skjerm.out("Mercedes", bredde);
    	skjerm.out("1985", bredde, Out.LEFT);
    	skjerm.outln("UE65660");
    
    	skjerm.out("Ford", bredde);
    	skjerm.out("1987", bredde, Out.LEFT);
    	skjerm.outln("ZE95523");
    
    	skjerm.out("Toyota", bredde);
    	skjerm.out("2003", bredde, Out.LEFT);
    	skjerm.outln("DK53401");
    
    	skjerm.outln(linje);
        }
    }
    

    (b) Løs samme oppgave ved hjelp av printf i stedet for EasyIO.  Du kan bruke %s for å skrive ut en String, og %-16 for å angi venstrejustering av String-en på 16 plasser, for eksempel:

    System.out.printf("%-16s", "Ford");

    class Biltabell {
        public static void main(String[] args) {
    	int bredde = 16;
    	String linje = "----------------------------------------";
    
    	// Overskrifter:
    	System.out.printf("%-16s%-16s%s\n", "Bilmerke", "Årsmodell", "Reg.nr.");
    
    	System.out.println(linje);
    
    	// Innmat i tabellen, linje for linje:
    	System.out.printf("%-16s%-16d%s\n", "Mercedes", 1985, "UE65660");
    	System.out.printf("%-16s%-16d%s\n", "Ford", 1987, "ZE95523");
    	System.out.printf("%-16s%-16d%s\n", "Toyota", 2003, "DK53401");
    
    	System.out.println(linje);
        }
    }
    


  3. Filer, store bokstaver, og args:  kap. 3 (side 54), og oppg. 7 (side 71)
    (a) Filkopi: Lag et program som leser inn en fil og kopierer innholdet over til en annen fil.  Filen skal leses inn ett tegn av gangen ved hjelp av inChar().  Du kan ta utgangspunkt i programmet vist under, fra side 54 i læreboka, som leser en fil et tegn av gangen, men skriver innholdet i filen ut på skjermen.  Endre programmet slik at det skriver ut til til en annen, nyopprettet fil, i stedet for å skrive ut på skjermen; endre klassenavnet til Kopi, og utvid programmet slik at det ber bruker taste inn filnavnene for de to filene (original og kopi).

    import easyIO.*;
    
    class Tegnleser {
        public static void main(String[] args) {
    	In fil = new In("minfil.txt");
    	int antall = 0;
    
    	while (!fil.endOfFile()) {
    	    char tegn = fil.inChar();
    	    System.out.print(tegn);
    	    antall++;
    	}
    	System.out.println("Antall tegn: " + antall);
        }
    }
    
    import easyIO.*;
    
    class Kopi {
        public static void main(String[] args) {
    	Out skjerm = new Out();
    	In tast = new In();
    
    	skjerm.out("Filnavn original: ");
    	String innfilnavn = tast.inLine();
    
    	skjerm.out("Filnavn kopi: ");
    	String utfilnavn = tast.inLine();
    
    	In innfil = new In(innfilnavn);
    	Out utfil = new Out(utfilnavn);
    
    	int antall = 0;
    	while (!innfil.endOfFile()) {
    	    char tegn = innfil.inChar();
    	    utfil.out(tegn);
    	    antall++;
    	}
    	innfil.close();
    	utfil.close();
    	System.out.println("Antall tegn kopiert: " + antall);
        }
    }
    
    KJØREEKSEMPEL:
    > java Kopi
    Filnavn original: minfil.txt
    Filnavn kopi: minfil2.txt
    Antall tegn kopiert: 38
    

    (b) Store bokstaver: Ta utgangspunkt i programmet vist ovenfor, og endre det slik at det skriver ut tegnene fra den innleste filen til skjerm, men med alle små bokstaver konvertert til store bokstaver.  Følgende setninger viser hvordan man kan konvertere innholdet i en char-variabel c til store eller små bokstaver:
    char c = ’x’;
    char c2 = Character.toUpperCase(c);
    char c3 = Character.toLowerCase(c);
    
    import easyIO.*;
    
    class StoreBokstaver {
        public static void main(String[] args) {
    	In innfil = new In("minfil.txt");
    	int antall = 0;
    
    	while (!innfil.endOfFile()) {
    	    char tegn = innfil.inChar();
    	    tegn = Character.toUpperCase(tegn);
    	    System.out.print(tegn);
    	    antall++;
    	}
    	System.out.println("Antall tegn: " + antall);
        }
    }
    
    KJØREEKSEMPEL:
    > java StoreBokstaver
    A
    CANIS FAMILIARIS BETYR HUND
    15
    3.14
    Antall tegn: 38
    

    (c) Antall ord: Når man skriver artikler for publisering er det ofte grenser for hvor mange ord de kan inneholde.  Lag et program som teller antall ord i en fil.  Filnavnet kan du be bruker taste inn når programmet starter.

    import easyIO.*;
    
    class AntallOrd {
        public static void main(String[] args) {
    	In tast = new In();
    
    	System.out.print("Filnavn (for telling av antall ord): ");
            String filnavn = tast.inLine();
    	In innfil = new In(filnavn);
    
    	int antall = 0;
    	while (!innfil.endOfFile()) {
    	    innfil.inWord();
    	    antall++;
    	}
    	System.out.println("Antall ord: " + antall);
        }
    }
    
    KJØREEKSEMPEL:
    $ java AntallOrd
    Filnavn (for telling av antall ord): minfil.txt
    Antall ord: 3
    

    (d) args[]: Lag deretter en annen utgave av programmet som tar filnavnet fra første kommandolinjeargument, dvs. args[0].  "Kommandolinjeargumenter" er evt. tilleggs-ord som bruker angir i selve java-kommandolinjen når hun kjører programmet.  For eksempel, hvis bruker starter programmet med følgende kommando:

    $ java AntallOrd fil.txt ord2
    
    ...så putter Java de to siste tilleggs-ord i arrayen String args[] (som vi har sett øverst i alle programmene våre til nå uten å bruke det).  I dette tilfellet vil Java sørge for at args[0] får verdien "fil.txt" når programmet starter, og args[1] får verdien "ord2".

    import easyIO.*;
    
    class AntallOrd {
        public static void main(String[] args) {
    	In innfil = new In(args[0]);
    	int antall = 0;
    
    	while (!innfil.endOfFile()) {
    	    innfil.inWord();
    	    antall++;
    	}
    	System.out.println("Antall ord: " + antall);
        }
    }
    
    KJØREEKSEMPEL:
    > java AntallOrd minfil.txt
    Antall ord: 3
    


  4. Filbehandling linje for linje:  (eksempel side 55 i læreboka)
    (a) Studér følgende program, fra side 55 i læreboka, som leser en fil en linje av gangen, og skriver den ut på skjermen med linjenummer foran i hver linje.  Endre programmet slik at det i stedet for å skrive ut alle linjene bare skriver ut en melding til slutt om hvor mange linjer og hvor mange tegn filen inneholder.  For å telle antall tegn kan du bruke en variabel som summerer verdiene av linje.length()

    import easyIO.*;
    
    class Linjeleser {
        public static void main(String[] args) {
    	In fil = new In("minfil.txt");
    	int linjenummer = 0;
    
    	while (!fil.endOfFile()) {
    	    String linje = fil.readLine();
    	    linjenummer++;
    	    System.out.println(linjenummer + " " + linje);
    	}
        }
    }
    
    import easyIO.*;
    
    class Linjeleser {
        public static void main(String[] args) {
    	In innfil = new In("minfil.txt");
    	int antLinjer = 0;
    	int antTegn = 0;
    
    	while (!innfil.endOfFile()) {
    	    String linje = innfil.readLine();
    	    antLinjer++;
    	    antTegn += linje.length() + 1;  // + 1 for å ta med linjeskiftet "\n"
    	}
    	System.out.println("Antall linjer: " + antLinjer);
    	System.out.println("Antall tegn: " + antTegn);
        }
    }
    
    KJØREEKSEMPEL:
    > java Linjeleser
    Antall linjer: 4
    Antall tegn: 38
    

    (b) Ta utgangspunkt i programmet vist ovenfor, og endre det slik at det skriver ut linjene på skjerm, men med alle små bokstaver konvertert til store bokstaver.  Følgende setninger viser hvordan man kan konvertere bokstavene i en String-variabel til store bokstaver:
    String s = "Jeg ER 18 år";
    String s2 = s.toUpperCase();
    // Nå er s2 tekststrengen "JEG ER 18 ÅR"
    

    import easyIO.*;
    
    class Linjeleser {
        public static void main(String[] args) {
    	In innfil = new In("minfil.txt");
    	int linjenummer = 0;
    
    	while (!innfil.endOfFile()) {
    	    String linje = innfil.readLine();
    	    linje = linje.toUpperCase();
    	    linjenummer++;
    	    System.out.println(linjenummer + " " + linje);
    	}
        }
    }
    
    KJØREEKSEMPLER:
    > java Linjeleser
    1 A
    2 CANIS FAMILIARIS BETYR HUND
    3 15
    4 3.14
    


  5. Public/private, klasse-/objekt-variabler:  i kapittel 8 (side 175)
    (a) Private: Vi tar en ny titt på bankkonto-programmet fra oppgave 4 i Ukeoppgaver 6.  Endre deklarasjonen av saldo i klassen Konto til å være private, og vis hvordan vi da kan få tak i saldo fra den andre klassen.  Tips: Bruk get-metoden.
    class KontoEksempel {
        public static void main(String[] args) {
            Konto k1 = new Konto();
    	k1.bestemKontonr();
    
            k1.settInn(500);
            System.out.println("Saldo er: " + k1.saldo);
    
            k1.taUt(300);
            System.out.println("Saldo er: " + k1.saldo);
        }
    }
    
    class Konto {
        int kontonr;
        int saldo;
        String eier, adresse;
        double rente = 2.5; // 2.5% per år
        static int nummer = 0; // Klassevariabel
    
        void bestemKontonr() {
    	nummer++;
    	kontonr = nummer;
        }
    
        void settInn(int innskudd) {
    	saldo = saldo + innskudd;
        }
    
        boolean taUt(int uttak) {
    	if (uttak > saldo) {
    	    return false;
    	}
    	saldo = saldo - uttak;
    	return true;
        }
    
        int getSaldo() {
    	return saldo;
        }
    }
    
    KJØREEKSEMPEL:
    $ java KontoEksempel
    Saldo er: 500
    Saldo er: 200
    
    Endre følgende linje i klassen Konto fra:
            int saldo
    Til:
            private int saldo
    
    Og endre følgende linje som står to steder i klassen KontoEksempel fra:
            System.out.println("Saldo er: " + k1.saldo);
    Til:
            System.out.println("Saldo er: " + k1.getSaldo());
    

    (b) Klassevariabler: De fleste metoder og variabler i programmet ovenfor er objekt-variabler og objekt-metoder, men det er én klasse-variabel og én klasse-metode i programmet.  Finn disse, og diskuter hvordan de er annerledes enn objekt-variantene.  Hvordan fungerer klasse-variabelen som står i programmet, og hva ville skjedd hvis vi tok bort nøkkelordet static fra deklarasjonen av variabelen?

    Klassemetoden er metoden main i klassen KontoEksempel, og klassevariabelen er int nummer i klassen Konto.

    Forskjellen mellom klassevariabler og objektvariabler er at hvert objekt man oppretter av klassen (f.eks. ved hjelp av nøkkelordet new) får sin egen utgave av objektvariablene (som kan ha sin egen verdi), men ikke av klassevariablene. Det finnes bare én verdi i hver klassevariabel som vil alltid være den samme i alle objektene av klassen.

    Forskjellen mellom klassemetoder og objektmetoder er at klassemetoder bare kan aksessere klassevariabler (og evt. lokale variabler deklarert i selve metoden) og klassemetoder, men ikke objektvariabler eller objektmetoder; mens objektmetoder derimot kan aksessere både objekt- og klasse-variabler og -metoder.

    Hvis vi tar bort nøkkelordet static fra deklarasjonen av variabelen nummer i klassen Konto, så gjør vi den om til fra å være klassevariabel til å være en vanlig objektvariabel.  Det vil føre til at hvert kontoobjekt vil nå få sin egen kopi av variabelen, med egen verdi, som dermed vil få startverdi 0 i alle Konto-objektene.  Det vil resultere i at alle kontoer får samme kontonummer, som vil være 1 i alle kontoer.


Oppgaver til terminaltimen

  1. Tabell, filer, args, private, og static:
    (Samme oppgave som nr. 1 til nr. 5 for teoritimen.)


  2. Fortsett med Oblig 3.
    NB! I Oblig 3 er det veldig viktig at du skriver din egen løsning selv.  Det er lov å diskutere med andre hvordan du tenker å gå frem for å løse deloppgavene, men det er ikke lov å kopiere programbiter fra obliger skrevet av andre, heller ikke hvis du deretter endrer på disse programbitene.  Hvis du samarbeidet tett med noen andre skriv navnene deres i en kommentar øverst i programmet.  Andre typer kilder bør også nevnes hvis det ikke er kurs-websidene, lærebøker, eller lærere og orakler i kurset.


  3. Ukens nøtt er Oblig 3-Pi.


Tibakemelding om dette oppgavesettet kan du skrive i bloggen eller sende på mail til josek [a] ifi.uio.no