Komplett pensumoversikt for objektorientert programmering ved UiO — med forklaringer, sentrale begreper, eksamenstips og vanlige fallgruver. Eksamensoptimalisert basert på tidligere eksamener.
IN1010 Objektorientert programmering er 10 studiepoeng ved Institutt for informatikk, Universitetet i Oslo, og går i vårsemesteret rett etter IN1000. Faget undervises i Java og dekker arv og polymorfisme, grensesnitt og abstrakte klasser, egendefinerte datastrukturer med lenkede lister, iteratorer, generics, unntakshåndtering, rekursjon og flertråding med monitorer.
Denne studieguiden er skrevet etter en gjennomgang av alle eksamensoppgavene og sensorveiledningene fra 2015 til og med konteeksamen 2024 — seksten eksamenssittinger. Det som slår en når man leser dem etter hverandre, er hvor forutsigbar eksamen er. Siden 2022 har settene fulgt nesten nøyaktig samme mal, og bare konteksten skifter: et tog med vogner og lokomotiv, en flyformasjon, en kolonne av selvkjørende biler, et hotell med rom i flere etasjer, en konkurransegruppe med seilfly. Bak kulissene er oppgaven den samme hver gang.
Malen ser slik ut. Du får beskrevet en samling ting som naturlig deler seg i et hierarki, og skal først tegne klassehierarkiet med en abstrakt toppklasse og ett grensesnitt (5–6 poeng). Så skal du programmere de samme klassene med extends, implements, super() og final-felt (15–16 poeng). Deretter kommer den store oppgaven: en beholderklasse som holder objektene i en egen (dobbelt)lenket liste, med metoder for å legge til, søke, ta ut og sette inn foran et gitt element, samt en iterator (20–40 poeng). Så kommer en runde med rekursjon, unntak og array-behandling (16–22 poeng). Og til slutt tråder: to trådklasser som implementerer Runnable, og en monitor med ReentrantLock og Condition (10–36 poeng).
To begrensninger preger alt du skriver. For det første: du får ikke bruke samlingene i java.util. Bare java.lang, pluss Iterator, Iterable, NoSuchElementException og låseklassene når oppgaven gjelder tråder. For det andre: pekerne skal ligge i dataobjektene selv, ikke i egne node-objekter. Sensorveiledningene sier det rett ut: «Det skal ikke brukes Node-objekter.» Har du øvd på lenkede lister i en generell Node-stil, må du legge om.
Sensorveiledningene forteller også hva som ikke teller. Manglende semikolon og krøllparenteser ignoreres om koden ellers gir mening. Import-setninger trenger du ikke skrive. Tilgangsmodifikatorer (public, private, protected) er uttrykkelig sagt å være uviktige. Småfeil i gjenkjennbare navn ignoreres. Det som derimot gir trekk, er «bruk av beholder på en måte som tyder på manglende forståelse» — altså kode som er hentet fra et annet program og limt inn uten at den passer. Skriv heller enkelt og riktig enn avansert og halvferdig.
De ni kapitlene under følger denne strukturen. Hvert kapittel har gjennomgåtte kodeeksempler i den stilen sensor forventer, en oppsummering, konkrete eksamenstips hentet fra sensorveiledningene, og en liste over de feilene som faktisk koster poeng.
Oppgave 1 og 2 på hver eksamen er klassehierarki: abstrakt toppklasse, to-tre nivåer, ett grensesnitt. extends med super() først i konstruktøren, abstrakte metoder, dynamisk binding etter faktisk type, og instanceof med nedkasting for å hente ut undertyper fra en liste deklarert med supertypen.
Oppgave 1 på hver eneste IN1010-eksamen de siste årene lyder omtrent: «Tegn et klassehierarki for situasjonen beskrevet over.» Oppgave 2 lyder: «Programmer klassene og grensesnittet du tegnet.» Til sammen er dette 20 poeng av 100 — mer enn en femtedel av karakteren, og de enkleste poengene på hele settet. Scenarioene varierer (tog med vogner og lokomotiv, fly i en formasjon, biler i et kollektiv, seilfly i en konkurransegruppe, rom på et hotell), men strukturen er identisk: én abstrakt klasse på toppen, to eller tre nivåer under, og ett grensesnitt på siden som noen av klassene implementerer.
Arv skrives med extends. En subklasse arver alle felt og metoder fra superklassen, og Java har bare enkel arv: en klasse kan ha nøyaktig én superklasse (men implementere mange grensesnitt).
abstract class Skinnegaende {
final String id;
final int sporvidde;
Skinnegaende neste, forrige; // pekere for dobbeltlenket liste
Skinnegaende(String id, int sporvidde) {
this.id = id;
this.sporvidde = sporvidde;
}
String hentId() { return id; }
int hentSporvidde() { return sporvidde; }
}
abstract class Vogn extends Skinnegaende {
final int lengde;
Vogn(String id, int sporvidde, int lengde) {
super(id, sporvidde); // MÅ stå først
this.lengde = lengde;
}
}
class Passasjervogn extends Vogn {
final int maksPassasjerer;
Passasjervogn(String id, int sporvidde, int lengde, int maks) {
super(id, sporvidde, lengde);
this.maksPassasjerer = maks;
}
}
Kallet på super(...) må stå som første setning i konstruktøren. Hvis du utelater det, setter Java automatisk inn et kall på superklassens konstruktør uten parametere — og finnes ikke en slik, får du kompileringsfeil. Sensorveiledningene nevner «konstruktørene skal kalle på super() på riktig måte» som eget poengkriterium hvert eneste år.
En klasse merkes abstract når det ikke skal finnes objekter av den. På eksamen står det gjerne «alt materiell er enten av klassen A eller klassen B» — det betyr at superklassen skal være abstrakt. En abstrakt klasse kan ha felt, konstruktør og ferdig implementerte metoder; den kan i tillegg deklarere abstract-metoder uten kropp, som subklassene må implementere.
abstract class Fly {
abstract int hentMTOW(); // ingen kropp, subklassene må fylle den
String beskriv() { // vanlig metode, arves ferdig
return "Fly med MTOW " + hentMTOW() + " kg";
}
}
Merk at beskriv() kaller hentMTOW() selv om den ikke finnes i Fly. Det virker fordi kallet bindes dynamisk til den faktiske subklassens versjon på kjøretid.
Når en subklasse deklarerer en metode med samme signatur som superklassen, overskriver den superklassens versjon. Java velger da metode etter objektets faktiske klasse, ikke etter referansevariabelens deklarerte type. Dette kalles dynamisk binding, og det er kjernen i polymorfi.
class Dyr {
String lyd() { return "..."; }
}
class Hund extends Dyr {
String lyd() { return "voff"; }
}
Dyr d = new Hund(); // deklarert type Dyr, faktisk type Hund
System.out.println(d.lyd()); // skriver "voff", ikke "..."
Regelen er verdt å pugge i to deler. Hva du har lov til å kalle bestemmes av den deklarerte typen: d.lyd() kompilerer bare fordi Dyr har en lyd(). Hvilken kode som faktisk kjører bestemmes av den faktiske typen. Felt oppfører seg ikke slik: felt bindes statisk, etter deklarert type. Derfor skal du overskrive metoder, aldri prøve å «overskrive» felt.
Fordi listene på eksamen er deklarert med superklassetypen, må du ofte finne ut hva et element egentlig er. Da bruker du instanceof og kaster ned (downcast):
for (Skinnegaende p = forste; p != null; p = p.neste) {
if (p instanceof Passasjervogn) {
Passasjervogn pv = (Passasjervogn) p;
System.out.println(pv.maksPassasjerer);
}
}
Å kaste opp (fra subklasse til superklasse) er alltid trygt og skjer automatisk. Å kaste ned kan feile: hvis objektet ikke er av den typen, får du ClassCastException på kjøretid. Derfor står instanceof-testen alltid rett foran. Nyere Java tillater den kompakte formen if (p instanceof Passasjervogn pv), som tester og deklarerer i én operasjon, men på eksamen er den todelte formen tryggest — den er det sensor forventer å se.
Tegneoppgaven gir 5–6 poeng og tar to minutter når du vet hva sensor ser etter. Kriteriene som gjentas i sensorveiledningene er: den abstrakte klassen skal stå øverst og være merket abstrakt; alle klassene skal være med; pilene skal peke opp mot superklassen; grensesnittet skal være tegnet som grensesnitt (typisk stiplet boks eller boks merket «interface»), og de klassene som implementerer det, skal ha en pil opp til det. Sensorveiledningene sier også at «andre fornuftige hierarkier er også OK» så lenge du begrunner valget — så ikke bli stående og gruble; velg noe fornuftig, skriv en linje om hvorfor, og gå videre.
Slik ser en typisk oppgave 2-besvarelse ut, komprimert:
interface Motordrevet {
int trekkraft();
}
abstract class Fly {
final String id;
final int mtow;
Fly neste; // for enkeltlenket liste
Fly(String id, int mtow) { this.id = id; this.mtow = mtow; }
String hentId() { return id; }
int hentMTOW() { return mtow; }
}
abstract class MotorFly extends Fly implements Motordrevet {
final int kw;
MotorFly(String id, int mtow, int kw) { super(id, mtow); this.kw = kw; }
public int trekkraft() { return kw; }
}
class PassasjerFly extends MotorFly {
final int maksPassasjerer;
PassasjerFly(String id, int mtow, int kw, int maks) {
super(id, mtow, kw);
this.maksPassasjerer = maks;
}
}
class SeilFly extends Fly {
final int synkehastighet; // cm/s
SeilFly(String id, int mtow, int synk) { super(id, mtow); this.synkehastighet = synk; }
}
Legg merke til at MotorFly både arver og implementerer, at trekkraft() er public fordi grensesnittmetoder alltid er det, og at pekeren neste ligger i toppklassen slik at hele hierarkiet kan hektes sammen i én liste.
Nøkkelformler
Vanlige feil
Eksamenstips
Laster...