TDDC30 Programmering i Java, datastrukturer och algoritmer
Laboration 1 - Grunderna för OOP i Java
Inledning
Börja självklart med att läsa igenom hela laborationshandledningen så att du får en översikt över laborationens struktur och omfång och vad som ska göras och hur det ska redovisas.Laborationen går ut på att lära sig grunderna för objektorienterad programmering, samt motsvarande språkkonstruktioner i Java.
Laborationen består av olika steg där du får öva på bl.a. följande begrepp inom OOP:
- Klasser och instanser
- Meddelandesändning
- Polymorfism
- Arv
- Instansvariabler och klassvariabler (static-variabler)
- Metoder och klassmetoder (static-metoder)
- Inkapsling och "information hiding" (skydda implementationen)
- Skyddsnivåerna private, protected, public
Under labben kommer du att modifiera och bygga på klasserna i programmet på olika sätt. Det kan vara klokt att spara undan koden från de tidigare stegen genom att skapa kopior av filerna. Eclipse låter dig göra detta direkt via "Package Explorer" panelen.
Laborationshandledningens struktur
Laborationen är indelad i 4 steg. Varje steg exemplifierar olika begrepp.I varje steg finns litet kodexempel. Det kan vara exempel på hur er kod ska se ut eller annan kod som ska kunna använda er kod.
Steg 1 och 2 följs i sin helhet för att fullborda dom stegen.
Steg 3 inleds med en diskussion om olika företeeleser som ni kan testa på er egen kod. Själva uppgiften kommer litet längre fram under rubriken "Uppgift".
Steg 4 består i sin helhet av en uppgiftsbeskrivning.
Det program som slutligen ska redovisas är resultatet efter steg 4.
Varje steg avslutas med ett antal frågor. Dessa ingår inte i kodskrivandet eller någon annan skriftlig redovisning. Frågorna ska ni själv använda som kontrollpunkter att ni förstår vad som händer. Frågorna ska ni också kunna besvara vid den muntliga redovisningen.
Allra sist finns en länk till lämpliga övningar.
Examination och inlämning
Examinationen består av två moment. Inlämning av labben per mail, och en muntlig redovisning.Be din lärare att komma och gå igenom labben med er när ni är klara. Ni får då svara på några av frågorna under de olika stegen. Observera att ni ska kunna redogöra för all kod och alla frågor individuellt. Det är inte godtagbart att en person i gruppen har gjort det mesta av jobbet.
Som en del av den muntliga redovisningen ska ni visa en körning av ert program i Eclipse. Ni ska även visa upp dokumentationen för labben som ni skapat med hjälp av javadoc.
Bifoga följande i ett mail till din labassistent:
- Hela Eclipse-projektet samt källkoden enligt de generella instruktionerna. Alla klasser och publika metoder ska vara kommenterade med javadoc-kommenterar. Koden ska följa kodstandarden och vara enhetligt indenterad.
- En separat sida där du anger hur mycket tid du har använt för uppgiften (för din grupp), och några kortfattade reflektioner kring uppgiften. Det kan t.ex vara synpunkter på uppgiften och feedback till lärarna, vad som känns oklart, begrepp som är svåra, osv. (bedöms ej)
Steg 1 - polymorfism
Skapa klasser för att representera djur i allmänhet, hundar och katter. Klasserna Dog och Cat ska ärva från klassen Animal. Klasserna ska ha metoder enligt följande:
Animal
introduceYourself() - ska skriva ut texten:
"Morr. Jag är ett djur."
Cat (ärver Animal)
introduceYourself() - ska skriva ut texten:
"Mjau. Jag är en katt som heter X"
Dog (ärver Animal)
introduceYourself() - ska skriva ut texten:
"Vov. Jag är en hund som heter X"
Lägg varje klass i en egen fil.
Använd klassen nedan för att testa dina klasser. För några av klasserna behöver du även skapa konstruktorer för att detta ska fungera.
public class Lab1Steg1{
public static void main(String[] args){
Animal[] allAnimals;
int i;
allAnimals = new Animal[3];
allAnimals[0] = new Cat("Kurre");
allAnimals[1] = new Dog("Vilma");
allAnimals[2] = new Cat("Bamse");
i = 0;
while (i < allAnimals.length){
allAnimals[i].introduceYourself();
i = i + 1;
}
}
}
Lägg även denna klass i en egen fil, som alltså ska heta Lab1Steg1.java
Frågor:
- Vad blir det för utskrift när man kör ovanstående kod?
- Vad menas med polymorfism?
- Hur fungerar polymorfismen i ovanstående program?
- Metoden introduceYourself i Animal verkar ju aldrig anropas? Varför inte?
- Kommentera bort metoden introduceYourself i Dog. Vad händer nu när du kör programmet?
- Var sparas namnet för instanserna av Cat och Dog? (I vilken/vilka klasser har du lagt instansvariabeln som refererar till namnet på djuret? I både Cat och Dog, eller bara i Animal?)
- Hur fungerar koden i testprogrammet?
- Hur fungerar en array?
- I ovanstående program har en while-loop använts för att stega igenom fältet och skriva ut information om djuren, men det finns en bättre lämpad loop-sats här. Vilken är det?
Steg 2 - instansvariabler och klassvariabler
Lägg till en publik instansvariabel i klassen Animal av typen int som heter age.
Modifiera metoderna introduceYourself i klasserna Animal, Cat och Dog så att de också skriver ut hur gammalt djuret är. Här är ett exempel på kod för klassen Cat:
public void introduceYourself()
{
System.out.println("Mjau. Jag är en katt som heter " + this.name);
System.out.println("och jag är " + this.age + " år gammal.");
}
Testkör sedan med följande kod:
public class Lab1Steg2{
public static void main(String[] args){
Animal kurre = new Cat("Kurre");
Animal vilma = new Dog("Vilma");
kurre.age = 6;
vilma.age = 3;
kurre.introduceYourself();
vilma.introduceYourself();
}
}
Frågor:
- Vad blir utskriften?
- Förklara hur det kommer sig att instansvariabeln age i Animal kan användas i Cat och Dog när den är deklarerad i Animal.
- Vad består egentligen en instans av?
- Och vad består en klass av?
- Vad är skillnaden mellan en klass och en instans?
- Ändra deklarationen av instansvariabeln age i Animal till en klassvariabel genom att använda static på det här sättet:
public static int age; - Vad blir resultatet av utskriften nu? Varför då?
- Det kan hända att du får varningar från kompilatorn om att du borde accessa age som Animal.age, men det bör gå att köra programmet i alla fall. Ändra annars referenserna till age i koden till Animal.age.
- Var sparas värdet på en instansvariabel?
- Var sparas värdet på en klassvariabel?
- Vad refererar variabeln this till?
Steg 3 - publika vs. privata variabler
Här får du lära dig varför man ska skydda representationen (instansvariablerna) och du får också lära dig en del om konstruktorer, och prova att skriva en klassmetod (static-metod).Själva uppgiften för Steg 3 kommer i slutet av steget.
Det är mycket osnyggt att använda publika variabler på det sätt vi gjorde i Steg 2. Anledningen är att koden blir känslig för ändringar.
Antag att vi vill ändra representerationen av hur gammalt ett djur är från age till birthyear. Så här skulle det kunna se ut i klassen Animal i så fall:
public class Animal{
public int birthyear;
// variabeln age är borttagen
...
}
Plötsligt fungerar inte koden som använder variabeln age längre! Vi måste gå in och ändra i alla klasser där age används och skriva om koden så att den använder variabeln birthyear istället, och vi måste dessutom räkna ut åldern varje
gång vi vill veta denna genom att ta nuvarande årtal minus birthyearet.
I t.ex klassen Cat hade vi fått ändra till följande kod:
public void introduceYourself(){
System.out.println("Mjau. Jag är en katt som heter " + this.name);
System.out.println(
"och jag är " +
(2012 - this.birthyear) +
" år gammal.");
}
Hade vi haft en metod som hette getAge istället hade vi däremot bara behövt ändra på ett enda ställe i koden - i klassen Animal.
Så här skulle det kunnat ha sett ut i klassen Animal om vi hade gömt variabeln age från första början och kommit åt den via en metod istället:
public class Animal{
private int age;
public Animal(int age){
this.age = age;
}
public int getAge(){
return age;
}
}
Koden i Cat hade då t.ex kunnat se ut så här:
public class Cat extends Animal{
private String name;
public Cat(String name, int age){
super(age); // Observera anropet till superklassens, dvs. Animals konstruktor.
this.name = name;
}
public String getName(){
return this.name;
}
public void introduceYourself(){
System.out.println(
"Mjau. Jag är en katt som heter " + this.getName());
System.out.println(
"och jag är " + this.getAge() + " år gammal.");
}
}
Notera att i ovanstående kod gömmer vi också instansvariabeln name med private, för att den ska vara skyddad mot åtkomst av kod utanför klassen. Om man vill gå ett steg längre i förbättringarna kan man lägga name i Animal, och flytta metoden getName dit. Då slipper man ha variabeln name i både Cat och Dog, men man måste i så fall ge konstruktorn till Animal ett extra argument för nameet. Kanske har du redan gjort på det sättet i de tidigare stegen.
Nu när vi vill ändra representationen av age till birthyear, så behöver vi endast ändra i koden för Animal. Koden i övriga klasser fungerar utan ändringar eftersom vi kommer åt age via metoden getAge i Animal.
Koden i klassen Animal kan då se ut så här:
public class Animal{
private static int nuvarandeår = 2012;
private int birthyear;
public Animal(int age){
this.birthyear = Animal.nuvarandeår - age;
}
public int getAge(){
return Animal.nuvarandeår - this.birthyear;
}
}
I ovanstående kod används en klassvariabel för att hålla reda på vilket år som är det nuvarande året. Årtalet är ju rimligtvis det samma för alla djur. (Såvida de inte börjar hålla på med tidsresor...)
Uppgift.
Skriv om ditt program enligt de riktlinjer vi har gått igenom så att alla instansvariabler skyddas med private. Byt representationen av age så att födelsedatum sparas istället för age. Lägg alla instansvariabler i klassen Animal.
Koden för klasserna ska fungera med följande testprogram:
public class Lab1Steg3{
public static void main(String[] args){
Animal.setYear(2012); // Så här anropar man en klassmetod,
// dvs en statisk metod.
// Hur ska koden för setYear se ut?
Animal kurre = new Cat("Kurre", 6);
Animal vilma = new Dog("Vilma", 3);
kurre.introduceYourself();
vilma.introduceYourself();
Animal.setYear(2013); // Nu blir är ett nytt år för alla djur.
kurre.introduceYourself();
vilma.introduceYourself();
}
}
Det kan hända att kompilatorn klagar på koden i de tidigare testprogrammen (Lab1Steg1 och Lab1Steg2). Du kan i så fall ignorera det eller kommentera bort koden inuti dessa klasser så försvinner problemen.
Frågor:
- Vad är skillnaden mellan private och public?
- Hur kommer man åt instansvariabler som är deklarerade som private från andra klasser?
- Kan man komma åt private-variabler i subklasser?
- Vi har inte använt protected ännu, men vad innebär det?
- Vad innebär det att använda private, public och protected på metoder?
- Vad betyder this nu igen? När kan man utelämna this och när måste man ha med this?
- Vad betyder
super(age);i konstruktorn för klassen Cat i kodexemplet ovan? - Hur skriver man en klassmetod (en static-metod)?
- Vad skiljer en klassmetod från en vanlig metod?
- Kan man komma åt instansvariabler från en klassmetod?
- Slutligen: Varför är det känsligt att accessa en instansvariabel, men mycket mindre känsligt att anropa en metod?
Sammanfattningsvis: Att gömma implementationen (t.ex genom att göra instansvariablerna privata, men även metoder kan göras privata) är en av grundstenarna i OOP. Det är en del extra arbete att skriva metoder för att komma åt variablerna, men det är väl värt detta i lite större program, och i yrkesmässig programmering är det en självklarhet.
Steg 4 - relationer mellan objekt
Det här är det sista steget i labb 1 och här får du lära dig mer om relationer mellan objekt.Objekt kan hänga ihop med varandra. Det kallas för relationer. Ett hus kan t.ex innehålla ett antal olika djur. För att hålla reda på vilka djur som finns i huset, kan huset ha en lista över djuren. Ett djur i sin tur kanske kan ha en leksak, och det kanske kan ha en kompis, en relation till ett annat djurobjekt.
Uppgift
Din uppgift är att skriva ett program som har följande struktur:
- Du ska skapa två nya klasser, House och Toy. Det ska finnas en metod print() som skriver ut information om alla djur, deras vänner och leksaker.
- Instanser av typen House kan innehålla godtyckligt många djur.
- Klassen Toy har en sträng som är namnet på leksaken. Namnet sätts i konstruktorn, och kan kommas åt med den publika metoden getName().
- Instanser av kassen Animal (eller instanser av dess subklasser Dog och Cat) kan ha en kompis, som är ett annat djur. Animal kan även ha godtyckligt många leksaker.
Skapa även dokumentation för dina klasser med hjälp av javadoc. För att kunna göra det krävs att klasser och metoder kommenterats med javadoc-kommentarer.
Din kod för klasserna House, Animal, Dog, Cat, och Toy, skafungera med följande testprogram:
public class Lab1Steg4{
public static void main(String[] args){
// Sätt årtalet för djuren.
Animal.setYear(2012);
// Skapa några djur.
Animal kurre = new Cat("Kurre", 6);
Animal vilma = new Dog("Vilma", 3);
Animal bamse = new Cat("Bamse", 12);
Animal smilla = new Dog("Smilla", 1);
// Skapa leksaker.
Toy ball = new Toy ("Boll");
Toy shoe = new Toy ("Tuggsko");
Toy mouse = new Toy ("Platsmus");
// Skapa huset.
House house = new House();
// Skapa relationer mellan objekten.
house.addAnimal(kurre);
house.addAnimal(vilma);
house.addAnimal(bamse);
house.addAnimal(smilla);
kurre.setFriend(vilma);
vilma.setFriend(smilla);
bamse.setFriend(kurre);
kurre.addToy(ball);
kurre.addToy(mouse);
vilma.addToy(shoe);
vilma.addToy(ball);
// Skriv ut vad som finns i huset.
house.print();
}
}
Tips
Lite hjälp på vägen: så här kan metoden print i House se ut:
public void print(){
System.out.println("Följande djur finns i huset:");
for (Animal animal: animalList){
animal.print();
}
}
Ovanstående kod förutsätter att instansvariabeln animalList i House är av typen ArrayList<Animal>.
Det är en klass i Javas klassbibliotek och för att kunna använda den måste du importera den med:
import java.util.ArrayList;
Lägg import-satserna först i källkodsfilen, innan klassen. (En smidig genväg i Eclipse är att börja använda klassen och sen trycka ctrl+shift+o för att låta Eclipse försöka gissa vad som behöver importeras)
Nu fortsätter vi med en annan sak. Observera att man måste kolla att en variabel inte är null innan den används. Om man t.ex har en variabel friend i animal som kan vara null måste man testa att den har ett värde innan man kan använda den.
Här följer ett kodexempel som illustrerar detta:
class Animal extends Object{
private Animal friend;
public Animal(){
// Till att börja med har ett nytt djur ingen kompis.
this.friend = null;
}
public void setFriend(Animal animal){
this.friend = animal;
}
public void print(){
// Anropa introduceYourself för att skriva ut
// uppgifter om mig själv. Om this t.ex
// är en Cat-instans, kommer då metoden
// introduceYourself i Animal eller i Cat att
// anropas?
this.introduceYourself();
// Kolla att friend är skild från null innan
// meddelandet introduceYourself skickas.
if (friend != null){
System.out.println("Här är uppgifter om min kompis:");
friend.introduceYourself();
}
else{
System.out.println("Jag har ingen kompis.");
}
}
public void introduceYourself(){
System.out.println("Morr. Jag är ett djur.");
}
}
Om du utgår från ovanstående kodexempel för klassen Animal i ditt program, behövs i så fall metoderna setFriend och print även i subklasserna Dog och Cat?
Hur kan du bygga ut ovanstående kod för klassen Animal så att djurobjekt kan referera till leksaker? Vilka tillägg behövs i print för att namnet på leksakerna i djurets lista med leksaker ska skrivas ut?
Frågor:
- Hur skapar man en ArrayList?
- Hur lägger man in ett objekt i en ArrayList?
- Hur itererar man över elementen i en ArrayList? Finns det flera sätt?
- Kan du beskriva vilka meddelanden som skickas till vilka instanser och vilka metoder som anropas när print i House utförs?
Checklista för examinationen
Lärarna på kursen kommer att använda följande checklistor vid examinationen av uppgiften.Checklista för inlämnat material:
- Är alla delar inlämnade?
- Uppfyller programmet kraven i uppgiften?
- Följer koden kodstandarden? Avvikelser ger rest.
- Är koden enhetligt indenterad? Slarvig indentering ger omedelbart rest.
- Är alla klasser och publika metoder i koden kommenterade med javadoc-kommentarer?
- Är alla krav uppfyllda?
För den muntliga redovisningen ska alla i gruppen vara beredda att svara på följande frågor, samt frågorna för varje deluppgift.
- Kan du berätta vad en kodstandard är bra för?
- Ligger bara den kod som behövs i Cat och Dog, och den kod som blir likadan för både Cat och Dog i Animal? Vad är det som skiljer kod som måste ligga i Cat respektive Dog från den kod som kan delas via Animal?
- Vad är en klass?
- Kan du berätta vilka delar en klassbeskrivning i Java innehåller? Ge exempel med hjälp av en klass i ert program.
- Kan du förklara skillnaden mellan en klass och en instans?
- Kan du förklara vad instansvariabler är?
- Kan du förklara varför instanser har egna värden på instansvariabler, men delar på koden för metoderna?
- Kan du berätta vad synlighet innebär?
- Kan du ge exempel på hur "information hiding" används och vad det är bra för?
- Kan du förklara syntaxen för de olika kontrollstrukturer du har använt i ert program?
- Kan du visa ett exempel på en loop i ert program som itererar över en lista med objekt och förklara vad koden gör?
Extra övning
Efter lab 1 kan det vara lämpligt att göra någon av de rekommenderade övningarna som finns att hitta under "Kursmaterial". Detta kan speciellt vara bra om man tyckte att lab 1 var svår, eller om man blivit klar med den väldigt snabbt, och materialet för lab 2 ännu ej är genomgånget.
Framförallt rekommenderas övningar inom följande kategorier:
- Grundläggande Java
- Objektorientering
Även dessa kategorier kan vara aktuella:
- Problemlösning/allmäna övningar
- Grafik
Skapad för kursen HKGBB7
Modifierad av Sara Stymne 2005, 2006, Johan Janzén 2011, Jonas Lindgren
Sidansvarig: Jonas Lindgren
Senast uppdaterad: 2013-01-27
