430 likes | 580 Views
Java 11. Programmering med og uten objekter: hva er forskjellen? Noen generelle råd vedrørende oppgaveløsing Å lese fra fil Ole Christian Lingjærde Gruppen for bioinformatikk Institutt for informatikk Universitetet i Oslo. Programmering med og uten objekter. class Volum {
E N D
Java 11 Programmering med og uten objekter: hva er forskjellen? Noen generelle råd vedrørende oppgaveløsing Å lese fra fil Ole Christian Lingjærde Gruppen for bioinformatikk Institutt for informatikk Universitetet i Oslo
Programmering med og uten objekter class Volum { public static void main (String [] args) { System.out.println(Matte1.pi); System.out.println(Matte1.finnVolum(2.5)); Matte2 mat = new Matte2(); System.out.println(mat.pi); System.out.println(mat.finnVolum(2.5)); } } class Matte1 { static double pi = 3.14; static double finnVolum (double radius) { return 3 * pi * Math.pow(radius, 3) / 4; } } class Matte2 { double pi = 3.14; double finnVolum (double radius) { return 3 * pi * Math.pow(radius, 3) / 4; } } Statisk bruk av annen klasse Bruk objekt av annen klasse I denne klassen er alle variable og metoder statiske I denne klassen er alle variable og metoder ikke-statiske
Objektvariable og klassevariable alder: 0 navn : "" new Person(); class Person { static int maxAlder=100; int alder=0; String navn=""; } alder: 0 navn : "" new Person(); new Person(); når klassen refereres første gang alder: 0 navn : "" maxAlder: 100
Ole lærer å programmere og har laget programmet til venstre.Hva skriver programmet ut på skjermen? Oppgave 1 class StudentRegister { public static void main (String [] args) { Student s1, s2; s1 = new Student(); s1.init("Torjus", "S25332"); s2 = new Student(); s2.init("Avril", "S36336"); s1.skrivUt(); s2.skrivUt(); } } class Student { static String navn; static String studId; static void init(String n, String s) { navn = n; studId = s; } static void skrivUt() { System.out.println("Navn: " + navn); System.out.println("StudId: " + studId); } } Test programmet Editer programmet
this • Ethvert objekt inneholder en peker til seg selv: this • Noen ganger trenger vi å bruke this for å referere til objektvariablene: class Person { String navn = ”Andre Bjerke”; void byttnavn (String navn) { this.navn = navn; } } • Merk: vi kunne sluppet unna bruk av this ovenfor ved å kalle parameteren i metoden byttnavn for noe annet (f.eks. String n) - men ofte er det mer oversiktlig å gjøre som over. parameteren navn objektvariabelen navn
Pekerlikhet og innholdslikhet class Likhet { public static void main (String [] args) { Student stud1 = new Student("Petter", "01018926324"); Student stud2 = new Student("Petter", "01018926324"); if (stud1 == stud2) { System.out.println("Samme objekt"); } if ((stud1.navn == stud2.navn) && (stud1.fnr == stud2.fnr)) { System.out.println("Samme innhold"); } } } class Student { String navn; String fnr; Student (String navn, String fnr) { this.navn = navn; this.fnr = fnr; } }
Initialisering av variable i et objekt Anta at programmet vårt inneholder denne klassen: class Person { String navn; String fnr; } • Når vi har laget et objekt (med new) ønsker vi normalt å gi variablene i objektet fornuftige verdier med en gang. Noen muligheter: • Sett verdiene til objektvariablene direkte med prikk-notasjon: • Person p = new Person(); • p.navn = ”Petter”; • p.fnr = ”15108559879”; • Lag en init-metode i klassen: • Person p = new Person(); • p.init(”Petter”, ”15108559879”); • Benytt en konstruktør: • Person p = new Person(”Petter”, ”15108535738”);
Konstruktører - repetisjon En konstruktør er en spesiell type objektmetode som du kan bruke for å sikre at objektet starter sitt liv med fornuftige verdier i objekt-variablene. Konstruktører • har alltid samme navn som klassen de ligger i • utføres automatisk når et objekt opprettes med new • har ingen returverdi, men skal ikke ha void foran seg • overlastes ofte, dvs det er ofte flere konstruktører i en og samme klasse, hvor konstruktørene skiller seg fra hverandre ved antall parametre og/eller typen på parametrene.
Eksempel class TestSirkel { public static void main (String [] args) { Sirkel s1 = new Sirkel(); System.out.println("Radius: " + s1.radius); Sirkel s2 = new Sirkel(5.0); System.out.println("Radius: " + s2.radius); } } class Sirkel { double radius; Sirkel () { radius = 1.0; } Sirkel (double r) { radius = r; } } Konstruktør 1 Konstruktør 2
Når det ikke er noen konstruktør class Sirkel { double radius; Sirkel () { } } • Når en klasse ikke inneholder noen konstruktør, vil Java selv føye på en "tom konstruktør" uten parametre når programmet kompileres. Dermed er følgende to klassedeklarasjoner ekvivalente: • Merk: dersom klassen inneholder en eller flere konstruktører, vil Java ikke føye på en "tom konstruktør" uten parametre. class Sirkel { double radius; }
Oppgave: virker dette? Lar dette programme seg kompilere og kjøre? class Oppgave1 { public static void main (String [] args) { Bil b = new Bil(); } } class Bil { String regnr; Bil (String regnr) { this.regnr = regnr; } } Test programmet Editer programmet
Hvorfor programmere med objekter? • Med objekter kan vi ofte organisere våre data bedre. Eksempel: Informasjonen knyttet til en bestemt person er splittet opp i tre arrayer. String [] navn = new String[100]; String [] fnr = new String[100]; int [] tlfnr = new int[100]; class Person { String navn; String fnr; int tlfnr; } Person [] personreg = new Person[100]; Informasjonen knyttet til en bestemt person er samlet i et objekt. Bedre organisering – særlig når det er mye data å holde orden på.
Hvorfor programmere med objekter? • Med objekter kan vi samle data og operasjoner på dem. ... data om studenter ... ... data om ansatte ... ... data om kurs ... ... student-metoder ... ... ansatt-metoder ... ... kurs-metoder ... Her ligger alle data og alle metoder samlet ett sted class Student { ... data om studenter ... ... student-metoder ... } class Ansatt { ... data om ansatte ... ... ansatt-metoder ... } class Kurs { ... data om kurs ... ... kurs-metoder ... } Metoder og data som hører sammen er samlet. Lett å se hvilke metoder som jobber på hvilke data (modularisering av koden). Lett å kopiere alt som har med personer å gjøre (data+metoder) til andre programmer (gjenbruk).
Noen ord i etterkant av oblig 3 • Mange synes oblig 3 var vanskelig: • mye å gjøre • ny måte å tenke på • stort sprang fra de små eksemplene på forelesningene • De obligatoriske oppgavene er ikke i første rekke ment å teste kunnskapsnivået deres, men å videreutvikle det og forberede dere for nytt stoff (og naturligvis for eksamen). • Oppgavene har m.a.o. et helt annet siktemål enn f.eks. en deleksamen. • Derfor: det er høyst normalt om du synes oppgaven var mye jobb. Til gjengjeld har de fleste av dere formodentlig lært mye av det. • Vi skal nå se på noen generelle råd vedrørende løsing av slike oppgaver.
Råd 1: Skriv programmer "ovenfra og ned" • Bestem først hvilke klasser som skal være med (og deres rolle) • Fyll inn de mest sentrale variablene (de som utgjør datastrukturen), og skriv eventuelle nye klasser som trengs i datastrukturen • Skriv metodene på toppnivå (dvs de som styrer den overordnede programflyten, f.eks. en kommandoløkke). Kall på metoder ved behov, selv om disse ennå ikke er skrevet. • Skriv metodene du kaller på ovenfor, og fortsett til programmet er ferdig.
Eksempel: oblig 3 - Trinn 1 class Oblig3 { public static void main (String [] args) { Setereservasjon sr = new Setereservasjon(); sr.ordreløkke(); } } class Setereservasjon { Datamodell data = new Datamodell(); void ordreløkke() { // Gå i løkke hvor det skrives ut ordremeny, ordre leses, // og ordre utføres ved å kalle på passende metode. } // Her kommer en metode for hver av ordrene som kan utføres } class Datamodell { // Alle dataene + metoder for å manipulere og avlese dem }
Eksempel: oblig 3 - Trinn 2 class Datamodell { Sete[][] alleseter = new Sete[10][6]; // Her kommer metoder } class Sete { private String passasjernavn; private String billettnr; Sete (String navn, String nr) { passasjernavn = navn; billettnr = nr; } String finnNavn() {return passasjernavn;} String finnNr() {return billettnr;} }
Eksempel: oblig 3 - Trinn 3 class Setereservasjon { Datamodell data = new Datamodell(); In tastatur = new In(); Out skjerm = new Out(); void ordreløkke() { boolean fortsett = true; int ordre; while (fortsett) { ordre = lesOrdre(); switch (ordre) { case 1: nyttSete(); break; case 2: visSete(); break; ... osv ... case 6: fortsett = false; } } } // Her kommer en metode for hver av ordrene som kan utføres }
Eksempel: oblig 3 - Trinn 4 I klassen Setereservasjon: void nyttSete() { skjerm.out("Navn: "); String navn = tastatur.inWord("\n"); skjerm.out("Billettnr: "); String nr = tastatur.inWord("\n"); skjerm.out("Ønsket plassering: "); int rad = tastatur.inInt() - 1; int kol = finnKolNr(tastatur.inWord()); boolean ok = data.settInn(nr, navn, rad, kol); if (ok) { skjerm.outln("Setereservasjonen er utført"); } else { skjerm.outln("Setet er opptatt"); } } // ... og tilsvarende for de andre kommandoene brukeren kan gi
Eksempel: oblig 3 - Trinn 5 I klassen Datamodell: boolean settInn(String nr, String navn, int rad, int kol) { if (alleseter[rad][kol] == null) { alleseter[rad][kol] = new Sete(navn, nr); return true; } else { return false; } } // ... og tilsvarende for de andre kommandoene brukeren kan gi
Råd 2: skriv metoder "utenfra og inn" Når du skal skrive en metode, bestem først av alt hva som er input og output til metoden: • Input: Eventuelle parametre til metoden Kan også være klassevariable/objektvariable (eksempel: 2D-arrayen alleseter i eksemplet vi så på i stad). • Output: Eventuell returverdi fra metoden Kan også være modifikasjoner av klassevariable/objektvariable
Råd 3: Deleger oppgaver • Et viktig kjennetegn ved god programmering er at man delegerer oppgaver når det er naturlig - dvs kaller på metoder for å utføre deloppgaver. • Dermed blir hver enkelt del av programmet oversiktlig, og faren for feil minimeres. Det blir også lettere å finne feil senere. • Eksempel: • Hvert case i en kommandoløkke kaller på en metode som utfører den ønskede kommandoen, i stedet for at alt gjøres inni selve kommandoløkken. • NB: ikke overdriv delegering. Det er f.eks. ofte ikke naturlig at hvert eneste objekt har metoder for å lese fra terminal - det kan i mange tilfeller være bedre å gjøre slike ting sentralt (og heller kalle på metoder i objektene for å oppdatere deres variable).
Råd 4: formater alltid programkoden underveis • Dårlig: class Eksempel { public static void main (String [] args) { int x = 0; for (int i=0; i<10; i++) { x = x + 1; } if (x < 0) { System.out.println("Det var rart"); }}} • Bra: class Eksempel { public static void main (String [] args) { int x = 0; for (int i=0; i<10; i++) { x = x + 1; } if (x < 0) { System.out.println("Det var rart"); } } }
Råd 5: ikke endre på forutsetningene i oppgaven • Selv om mange oppgaver tar utgangspunkt i problemer fra virkeligheten, er de ikke nødvendigvis laget for å være særlig realistiske. • Eksempel fra oblig 3: • alle opplysninger forsvinner når programmet avsluttes - urealistisk • ingen sjekk på om et billettnr er gyldig - urealistisk • osv. • Ikke endre på forutsetningene i slike tilfeller - uansett hvor fristende det måtte være. Løs oppgaven slik den er formulert. • Hvis du mener oppgaven gir rom for flere tolkninger på et punkt: gjør dine egne (rimelige) forutsetninger - og skriv i oppgaven hva disse er.
Lese fra fil Vi må først importere pakken easyIO Vi åpner filen for lesing import easyIO.*; class LesFraFil { public static void main (String [] args) { In fil = new In("filnavn"); String s = fil.inLine(); System.out.println("Første linje var: " + s); } } Her leses hele første linje av filen
Lesemetoder 1 5 _ _ _ _ _ _ _ _ _ _ _ 5 3 5 _ _ _ 3 _ _ 5 2 2 _ _ Tabellen under viser resultatet av å kalle på inInt() tre ganger, eller inChar() tre ganger, osv.
Lese item for item • Metoder: • inInt() for å lese et heltall • inDouble() for å lese et flyttall • inWord() for å lese et ord • inWord("\n") for å lese første ikketomme linje • lastItem() for å sjekke om slutten av filen er nådd • Eksempel: fil hvor hver rad inneholder et heltall og en tekst: In fil = new In("fil.txt"); while (!fil.lastItem()) { int k = fil.inInt(); String s = fil.inWord("\n"); System.out.println(k + " " + s); }
Eksempel Program som leser en fil med desimaltall, hvor tallene er atskilt med blanke tegn og/eller linjeskift. Vi finner slutten av filen med lastItem(). import easyIO.*; class LesFraFil2 { public static void main (String [] args) { double [] x = new double[100]; // antar max 100 tall på fil In innfil = new In("fil.txt"); int ant = 0; while (!innfil.lastItem()) { x[ant] = innfil.inDouble(); ant = ant + 1; } // Nå ligger det verdier i x[0], x[1], ...., x[ant-1] } }
Lese linje for linje • Metoder: • inLine() for å lese en linje • endOfFile() for å sjekke om slutten av filen er nådd • Eksempel: In fil = new In("fil.txt"); while (!fil.endOfFile()) { String s = fil.inLine(); System.out.println(s); }
Eksempel Program som leser en tekstfil linje for linje: import easyIO.*; class LesFraFil3 { public static void main (String [] args) { String [] s = new String[100]; // antar max 100 linjer på fil In innfil = new In("fil.txt"); int ant = 0; while (!innfil.endOfFile()) { s[ant] = innfil.inLine(); ant = ant + 1; } // Nå ligger det tekststrenger i s[0], s[1], ...., s[ant-1] } }
Lese tegn for tegn • Metoder: • inChar() for å lese et tegn (også blanke og linjeskift) • endOfFile() for å sjekke om slutten av filen er nådd • Eksempel: In fil = new In("fil.txt"); while (!fil.endOfFile()) { char c = fil.inChar(); System.out.println(c); }
Eksempel Program som leser en tekstfil tegn for tegn og skriver ut på skjerm, sammen med antall tegn i filen: import easyIO.*; class LesFraFil4 { public static void main (String [] args) { In innfil = new In("fil.txt"); int antall = 0; while (!innfil.endOfFile()) { System.out.print(innfil.inChar()); antall++; } System.out.println("\nAntall tegn: " + antall); } }
lastItem og endOfFile • endOfFile() sjekker om siste tegn på fila er lest • lastItem() søker seg fram til første ikke-blanke tegn og returnerer true hvis slutten av fila nås og false ellers. • Eksempel: 3.253 12.65 -23.553 = ny linje (enter, carriage return) Fil som skal leses = blankt tegn (mellomrom, tabulator) Samme fil, slik den ser ut for datamaskinen lastItem() leser forbi disse og møter slutten av fila 3.253 12.65 -23.553 Her står lesemerket rett etter at første tall er lest Her står lesemerket rett etter at siste tall er lest
Når filens lengde er kjent • Når et program skal lese en fil, må det ha en mulighet til å avgjøre når slutten av filen nådd - ellers kan det oppstå en feilsituasjon. • Metodene lastItem() og endOfFile() kan benyttes til dette. • Noen ganger er filens lengde kjent på forhånd: • lengden er kjent før programmet kjøres • lengden ligger lagret i begynnelsen av filen Da kan vi i stedet benytte en for-løkke.
Eksempel: fil med kjent lengde Program som leser en fil med 10 desimaltall, hvor tallene er atskilt med blanke tegn og/eller linjeskift: import easyIO.*; class LesFraFil5 { public static void main (String [] args) { double [] x = new double[10]; In innfil = new In("fil.txt"); for (int i=0; i<10; i++) { x[i] = innfil.inDouble(); } // Nå kan vi evt. gjøre noe med verdiene i arrayen x } }
Nok at tallene er atskilt Programmet på forrige foil ville gitt akkurat samme resultat for alle disse filene: 15.2 6.23 3.522 3.6 8.893 -3.533 65.23 22.01 45.02 7.2 15.2 6.23 3.522 3.6 8.893 -3.533 65.23 22.01 45.02 7.2 15.2 6.23 3.522 3.6 8.893 -3.533 65.23 22.01 45.02 7.2
Eksempel: fil med lengde-info Program som leser en fil med desimaltall, hvor tallene er atskilt med blanke tegn og/eller linjeskift. Antall tall som skal leses ligger øverst i filen. import easyIO.*; class LesFraFil6 { public static void main (String [] args) { double [] x; // bestemmer ikke lengden ennå In innfil = new In(“fil.txt”); int lengde = innfil.inInt(); // nå vet vi lengden x = new double[lengde]; for (int i=0; i<lengde; i++) { x[i] = innfil.inDouble(); } // Nå kan vi evt. gjøre noe med verdiene i arrayen x } }
Eksempel: fil med sluttmerke Program som leser en fil med desimaltall, hvor tallene er atskilt med blanke tegn og/eller linjeskift. Slutten av filen er markert med tallet -999. import easyIO.*; class LesFraFil7 { public static void main (String [] args) { double [] x = new double[100]; // antar max 100 tall på fil In innfil = new In("fil.txt"); double siste = 0; int ant = 0; while (siste != -999) { siste = innfil.inDouble(); if (siste != -999) { x[ant] = siste; ant = ant + 1; } } // Nå ligger det verdier i x[0], x[1], ...., x[ant-1] } }
Lese en fil med mer komplisert format • Anta at vi skal lese en fil med følgende format: • Først er det en linje med 3 overskrifter (separert av blanke tegn) • Deretter kommer det en eller flere linjer, som hver består av et heltall, et desimaltall og en tekststreng (separert av blanke tegn) • Eksempel: • Dataene som leses skal programmet ta vare på for senere formål. Antall Pris Varenavn 35 23.50 Oppvaskkost 53 33.00 Kaffe 97 27.50 Pizza ... ... ... ... ... ...
Framgangsmåte • Den første linja er spesiell, og vi tenker oss her at den ikke er så interessant - vi ønsker bare å få lest forbi den. Det kan vi gjøre med inLine(). • De andre linjene har samme format, så vi kan lage en løkke hvor hvert gjennomløp av løkken leser de tre itemene på en linje. Vi bruker da henholdsvis inInt(), inDouble() og inWord(). • For å vite når filen er slutt, kan vi enten bruke endOfFile() eller lastItem(). Siden vi leser filen itemvis, er det mest naturlig å bruke lastItem(). Da får vi heller ikke problemer dersom det skulle ligge noen blanke helt på slutten av filen. • Vi hopper over detaljene.
Oppgave 1 • Lag et program som leser en fil og finner antall forekomster av et bestemt ord. Antallet skrives ut på skjermen.
Noen nyttige hjelpemidler (ikke pensum) • Sjekke om det finnes en fil med et bestemt navn: if (new File("filnavn").exists()) { System.out.println("Filen finnes"); } • Slette en fil: if (new File("filnavn").delete()) { System.out.println("Filen ble slettet"); } • Avgjøre hvilket filområde programmet ble startet fra: String curDir = System.getProperty("user.dir"); • Lage liste over alle filer og kataloger på et filområde: String [] allefiler = new File("filområdenavn").list(); Merk: klassen File ligger i pakken java.io som derfor må importeres først.