Lektion 2 Datateknik A, Java I, 5 poäng Syfte: Att förstå vad klasser och objekt är. Hur vi skriver egna enkla klasser för att sen skapa objekt av dessa. Prova på att använda metoder för att manipulera data i ett objekt. Lära sig att använda olika metoder för att läsa in data från tangentbordet. Att läsa: Kursboken, Kapitel 2 (sida 51 – 74) http://java.sun.com/docs/books/tutorial/java/concepts/object.html http://java.sun.com/docs/books/tutorial/java/concepts/message.html http://java.sun.com/docs/books/tutorial/java/concepts/class.html http://java.sun.com/docs/books/tutorial/java/data/objects.html Lektion 2 Java I, 5 poäng Vi ska i denna lektion fortsätta med vår översikt över Javaspråket. Vi kommer sen att gå in på det mest centrala i språket, nämligen klasser och objekt. Robert Jonsson ITM Östersund Sida 1 Lektion 2 Java I, 5 poäng I förra lektionen nämnde vi att det i Java följer med ett antal klassbibliotek (standardbibliotek) eller paket. Paket är en modulariseringsmekanism som används för att gruppera ihop klasser som ”hör samman” med varandra. Samtliga standarbibliotek ligger i olika paket och har namn som java.lang (klasser för de mest grundläggande funktioner i Javaspråket), java.util (stödklasser för stack, hashtabell, datum, tid etc.), java.io (klasser för fil och I/O-hantering), java.net (klasser för nätverkskommunikation och Internetklasser), java.awt (klasser för användargränssnitt). En utvecklare kan också skapa egna klassbibliotek och lägga in dem i paket. Paket lagras i en katalogstruktur baserad på ett pakets namn. När klasser från ett paket ska användas måste klasserna importeras från paketet genom en import-sats. Antingen kan alla klasser i paketet importeras, eller så kan endast de klasser som verkligen ska användas importeras. En import görs med nyckelordet import följt av namnet på paketet (och eventuellt klassen). Speciella synlighetsregler finns som bestämmer vad som ska vara åtkomligt inom och utanför ett paket. Man kan alltså mer eller mindre bestämma vilka som ska kunna använda klasserna i paketet (tolka inte vilka som vilka olika användare). Robert Jonsson ITM Östersund Sida 2 Lektion 2 Java I, 5 poäng Som vi nämnde i förra lektionen kan ett Javaprogram delas upp i två olika kategorier: applikationer och pplets. Vi kommer i denna kurs inte att titta speciellt mycket på Applets utan endast nämna vad det är. En Java applet har inte någon main-metod utan har istället en uppsättning metoder som webbläsaren kommer att anropa (metoderna har namn som init, destroy, start och stop som antyder när metoderna anropas). En Java applet har ett utseende som visas på den yta som den tilldelats av webbläsaren. Utseendet på denna yta definieras av appletens paint-metod, som innehåller anrop som grafiskt ritar upp ytans utseende. Programmet i bilden ovan, FirstApplet, importerar klasser från två andra paket, java.applet som innehåller klassen Applet och java.awt som innehåller Javas gränssnittsklasser (bl.a. grafikklasser). Klassen i programmet heter FisrtApplet (och sparas m.a.o. i en fil med namnet FirstApplet.java) och ärver en klass Applet (ett arv uttrycks genom nyckelordet extends följt av namnet på den klass som ärvs). Mer om arv senare. Den enda metod som klassen har är metoden paint, som bestämmer innehållet i appletens yta. Till metoden kommer ett objekt av typen Graphics att skickas och genom att anropa metoder på detta objekt bestäms utseende på den yta appleten ”äger”. Metoden drawString() tar tre s.k. argument. Det första är den text som ska skrivas ut, de två andra är på vilken x- respektive y-koordinat texten ska skrivas ut på (den yta vi ritar på har koordinaterna (0, 0) i övre vänstra hörnet). Metoden drawLine ritar ut en linje mellan en start- och slut koordinat. I det här fallet från (0, 0) till (100,100). Utöver dessa finns det mängder med andra metoder vi kan anropa. T.ex. för att byta färg, rita cirklar m.m. För att ta reda på vilka metoder som går att anropa kan man titta i Javas API. Leta efter klassen Graphics så ser du att det finns mängder med ”rit-funktioner”. I exemplen som följer med lektionen finns filen FirstApplet.java. Prova att kompilera den med javac och därefter köra den med java för att se vad som händer (felmeddelande). Robert Jonsson ITM Östersund Sida 3 Lektion 2 Java I, 5 poäng För att starta en applet krävs ett html-dokument där vi anger vilken klass (Applet) som ska köras, vi kan inte direkt köra .class-filen. Det minsta antalet s.k. html-taggar som krävs för att visa en applet är <html> och <applet>. I applet-taggen specificerar vi vilken klass vi vill köra samt vilken bredd och höjd vi tilldelar vår Applet. Det är denna yta som används för att rita upp innehållet. En Applet testas antingen genom en s.k. appletviewer i en utvecklingsmiljö, eller genom att de refereras i en HTML-sida som sedan läses in i en webbläsare. Observera att det är html-sidan som ska köras och inte class-filen. Bland exemplen till lektionen finner du filen FirstApplet.html. Prova att öppna den i en webbläsare. Du kan även prova på att använda appletviewer. Öppna ett kommandofönster och ställ dig i den katalog där html-filen ligger. Skriv sen: appletviewer FirstApplet.html Observera att html-sidan inte behöver ha samma namn som vår Applet. Robert Jonsson ITM Östersund Sida 4 Lektion 2 Java I, 5 poäng Appletviewern kan sägas vara en ”minimal” webbläsare för att testa Applets. Den startas med namnet på en HTML-fil och kommer sedan att läsa denna fil och starta ett fönster med den applet som refereras i HTML-filen. Via menyer kan sedan denna Applet stoppas, startas om, parametrar och annan information avläsas. En viktig varning i samband med exekvering av en applet i en appletviewer är att appleten inte exekveras med de säkerhetskontroller som utförs när samma Applet exekverar i en riktig webbläsare. Exempelvis kan en Applet som exekveras i appletviewer läsa och skriva filer på den lokala hårddisken vilket inte en applet som exekveras i en webbläsare får/kan göra. Robert Jonsson ITM Östersund Sida 5 Lektion 2 Java I, 5 poäng I stort sett alltid så måste program ha indata av något slag som programmet ska bearbeta. Hittills i våra program har vi endast behandlat utdata med hjälp av System.out.println. När ett program behöver indata av någon form är det vanligtvis användaren som får mata in data från tangentbordet. Det kan t.ex. ske genom att mata in text i olika textfält. De två sätt vi kommer att jobba med i denna kurs är genom att mata in text direkt från kommandofönstret och genom dialogrutor. Oavsett vilket sätt vi använder oss av så är det en komplicerad process. Att exakt förklara hur koden som används fungerar är något som vi inte klarar av med vad vi hittills gått igenom. Detta är ytterligare exempel på kod som ni får acceptera som den är. Det första alternativet är genom att använda ett objekt av klassen BufferedReader som läser de tecken som matas in från tangentbordet. All in- och utmatning (I/O) i Java kan generera fel av något slag, så när vi använder denna metod måste vi antingen fånga felet (catch) eller kasta det vidare (throw). Detta är exempel på s.k. felhantering som vi inte kommer att gå igenom i denna kurs. Med hjälp av identifieraren input kan vi nu läsa in det användaren skriver på tangentbordet till programmet. Detta gör vi genom att anropa metoden readLine(). Resultatet sparar vi i en sträng som vi ger namnet svar. Ta en titt på exemplet Input1.java för att testa inmatning med BufferedReader från kommandofönstret. Robert Jonsson ITM Östersund Sida 6 Lektion 2 Java I, 5 poäng Det andra alternativet är att använda klassen JOptionPane för att visa grafiska dialogrutor där användaren kan göra sina inmatningar. Med JOptionPane kan vi även skriva ut ett resultat i en dialogruta, istället för att skriva ut det på skärmen med System.out.println. I klassen JOptionPane finns det ett antal fördefinierade dialogrutor vi kan använda i vårt program. En av dessa kommer vi åt genom att anropa metoden showMessageDialog() på klassen JOptionPane. Denna metod kräver två argument (indata). Det första är var på skärmen dialogrutan ska visas. Här anger vi null vilket innebär att rutan kommer att centreras på skärmen. Det andra argumentet anger vilken text som ska visas i dialogrutan. Ex) JOptionPane.showMessageDialog(null, "Snart är det julafton"); Med en annan fördefinierad dialogruta kan vi låta användaren mata in text från tangentbordet som vi sen kan använda i programmet. Denna dialogruta kommer vi åt genom att anropa metoden showInputDialog(), också i klassen JOptionPane. Denna metod tar ett argument, vilket är en sträng med den text som ska visas i dialogrutan (frågan/uppmaningen till användaren). När användaren har skrivit in sin text och tryckt på knappen Ok (eller trycker enter) returneras det inskrivna som vi kan ta emot och lagra i en sträng. Ex)String svar = JOptionPane.showInputDialog("Vilken veckodag är det?"); När vi har ett program med ett grafiskt användargränssnitt är det viktigt att vi avslutar programmet genom att skriva System.exit(0); Argumentet 0 till metoden exit anger att programmet avslutades på ett korrekt sätt. Detta värde returneras till kommandopromten varifrån vi startade applikationen. Om vi vill kan vi ange olika felkoder istället för 0 om något fel har inträffat i programmet. Ta en titt på programmet Input2.java i exemplen för att se hur dialogrutor kan användas för in- och utmatning av data. Robert Jonsson ITM Östersund Sida 7 Lektion 2 Java I, 5 poäng Vi kommer nu att titta lite närmare på hur kompileringsprocessen i Java fungerar och hur den virtuella Javamaskinen jobbar. Java har, som du i det här laget redan vet, en mycket enkel struktur på sina filer där endast två filtyper används. En källkodsfil som innehåller en Javaklass måste ha samma namn som klassen följd av suffixet ”.java”. Utifrån denna källkodsfil genereras vid kompileringen en fil med bytekod som har samma namn som klassen följt av suffixet ”.class”. Eftersom klasser läses in dynamiskt under programmets exekvering och ingen länkning sker så behövs inga s.k. objekt-filer som länkas ihop till exekverbara filer. Endast två filer behövs, ”.java”- och ”.class”-filer. Fig. Endast ”.java”-filer med källkod och ”.class”-filer med bytekod behövs i Java. Den virtuella Javamaskinen startas med namnet på en klass och läser sedan in ytterligare klassers bytekod-filer allteftersom behov uppstår. Robert Jonsson ITM Östersund Sida 8 Lektion 2 Java I, 5 poäng Den virtuella Javamaskinen är den komponent som läser, tolkar och exekverar bytekod. Normalt är denna komponent skrivet för den aktuella plattformen (operativsystem och hårdvaruarkitektur) och som läser bytekoden, tolkar den, och gör anrop mot det underliggande operativsystemet. Den virtuella maskinen är alltså i högsta grad plattformsspecifik och måste skaffas om den inte redan finns inbyggd i aktuell plattform (i en del webbläsare finns en virtuell Javamaskin inbyggd). Robert Jonsson ITM Östersund Sida 9 Lektion 2 Java I, 5 poäng Specifikationen av den virtuella maskinen och formatet på bytekoden är mycket lik en ”virtuell” processor så den virtuella maskinen upprätthåller begrepp som programräknare, stack, cache etc. Den enda skillnaden är att den virtuella maskinen exekveras som ett program. Både specifikationen av formatet på bytekoden och den virtuella maskinen är helt öppen, och det är teoretiskt möjligt att skriva en bytekod-kompilator för ett annat språk än Java, men detta har visat sig svårare än många trott. Den virtuella Javamaskinen laddar dynamiskt in ”.class”-filer allt eftersom de behövs i programmet. För att exekvera en applikation anges namnet på den på en klass, varpå dess ”.class”-fil med bytekod läses in och dess main-metod exekveras av den virtuella maskinen. Allteftersom nya klasser refereras i applikationen kommer de att läsas in dynamiskt (allt eftersom). När en ”.class”-fil (Applet) läses från Interner så kommer koden i filen först att passas till en verifierare. Verifieraren är en speciell modul som går igenom filen och konstaterar att koden är ”riktig” och att inga farliga operationer utförs i koden (som att läsa filer på användarens dator). Det finns i JDK (den utvecklingsmiljö vi använder i kursen) ett program, javap, som är en disassembler av bytekod och som visar bytekoden på ett för människor läsbart format. Detta program kan tillsammans med specifikationen av den virtuella maskinen rent teoretiskt användas för att lära sig bytekod-formatet (varför man nu skulle vilja det?). Observera att man med javap INTE får tillbaka den källkod som finns i java-filen. Prova gärna att använda javap på någon av de class-filer du gjort för att se vad som händer. Robert Jonsson ITM Östersund Sida 10 Lektion 2 Java I, 5 poäng En av de vanligaste invändningarna mot Java är att program skrivna i Java har dåliga prestanda. Invändningarna har varit och är fortfarande i vissa fall berättigad. Och det är naturligtvis arrangemanget med en virtuell maskin som i huvudsak skapar dessa prestandakostnader. Den virtuella maskinen kan ju ses som en interpretator som lägger ett extra skal över den slutliga exekveringsmiljön. Språket Java som sådant har inga konstruktioner som gör att det skulle vara långsammare än andra språk. Att uppnå högre prestanda i ett program, oavsett språk eller miljö, kan angripas på flera olika sätt: Hög nivå: genom att förbättra sin design och använda mer effektiva datastrukturer och algoritmer. Ofta kan betydande prestandavinster uppnås genom detta, eftersom dålig grunddesign nästan alltid leder till program som är långsamma. Mellannivå: genom att använda diverse ”tricks” på språknivån som att deklarera variabler som konstanta (static), klasser och metoder som inte får ärvas eller omdefinieras (final), skapa få och stora klasser. Eller genom att använda shiftoperatorerna vid multiplicering och division. Låg nivå: genom att se till att den genererade koden kan optimeras och exekveras snabbare, d.v.s. att den fysiska exekveringen av programmet utförs snabbare på den aktuella plattformen. De nivåer som rekommenderas i första hand är den höga och den låga. Att använda tekniken på mellannivån bryter mot grundläggande principer för god programmering, och ger program som är svåra att underhålla, som inte är lika utbyggbara och förändringsbara. Robert Jonsson ITM Östersund Sida 11 Lektion 2 Java I, 5 poäng Vi ska nu titta på hur Java fått bättre prestanda genom att på låg-nivå förändra hur Javaprogram exekveras. En Just-In-Time (JIT) kompilator är en kompilator som översätter bytekod till plattformsspecifik kod som sedan exekveras på aktuell plattform (likt en kompilator av exempelvis C++). Kompileringen görs dock dynamiskt vid exekvering av ett program, där JITkompilatorn kopplas på den virtuella Javamaskinen. Det är alltså bytekoden som kompileras, ej den ursprungliga Javakällkoden. Den virtuella Javamaskinen är inte medveten om JIT-kompilatorn, och dess valiga säkerhetskontroller av bytekoden kommer fortfarande att utföras. JIT har diverse tricks för sig för att snabba upp exekveringen. Bl.a. kommer den att spara genererad kod för metoder så att själva översättningen från bytekod till plattformsspecifikkod endast sker en gång. Den gör enkla former av optimering av den genererade koden. Den kan tyvärr inte göra mer avancerad optimering eftersom den helt enkelt inte har tid – översättningen sker ju i samband med att koden ska exekveras (just in time). Mätningar har påvisat stora effekter på exekvering av Javaprogram. Exakta måtten varierar, men alltifrån: 3-4 ggr snabbare när användargränssnittskod exekveras till 40-50 ggr snabbare för beräkningar, loopar och upprepade metodanrop. Robert Jonsson ITM Östersund Sida 12 Lektion 2 Java I, 5 poäng Ingenting förhindrar att Javakod kompileras direkt till plattformsspecifik kod, d.v.s. att källkoden kompileras till en exekverbar fil på aktuell plattform (i Windows en exefil. Olika leverantörer av utvecklingsmiljöer som exempelvis Symantech har också lanserat sådana kompilatorer. En utvecklare kan då välja att antingen få bytekod genererad eller att få en plattformsspecifik exekverbar fil. Med en ren kompilator kan också mer avancerad optimering göras eftersom denna optimering då görs i samband med kompileringen, och det finns då hur mycket tid som helst till detta. Vissa hävdar att sådana kompilatorer ger program med jämbördiga prestanda som exempelvis kompilerad C++ kod. Sun avråder dock kraftigt mot denna lösning eftersom det bryter mot Javas plattformsoberoende. Robert Jonsson ITM Östersund Sida 13 Lektion 2 Java I, 5 poäng HotSpot är en ny teknik för att implementera virtuella maskiner som bygger på något som kallas adaptiv optimering. Den virtuella maskinen analyserar varje Javaprogram när den exekverar, och använder denna information till att omedelbart optimera de kritiska s.k. ”hot spots” som ett program har (de områden i programmet där de största datorresurserna förbrukas). Genom att optimera endast dessa kritiska områden frigörs tid att göra mer avancerade optimeringar. Med adaptiv optimering kan bättre optimeringar göras än med en ”ren” kompilator, eftersom mer information finns om programmets exekvering (en ”ren” kompilator studerar endast källkoden före programmets exekvering). Sun påstår att i och med HotSpot kommer Javaprogram inte att ha någon prestandakostnad alls jämfört med plattformsspecifika program. Vilket vi dock ska ta med en nypa salt. Robert Jonsson ITM Östersund Sida 14 Lektion 2 Java I, 5 poäng För att kort sammanfatta det hela så: Javaspråkets grundblock är klasser som beskriver egendefinierade ”typer” och objekt (förekomster) av dessa klasser. En klass beskrivs i termer av instansvariabler (datavariabler) och metoder. En klass beskrivs i en ”.java”-fil med samma namn som klassen. Java har också en mängd fördefinierade primitiva typer som int, long, char, boolean, float och double. För dessa datatyper finns olika typer av operatorer som exempelvis aritmetiska, jämförelse, booleska, bit, skift och tilldelningsoperatorer. Exekveringsflödet i ett Javaprogram styrs av kontrollflödeskonstruktioner, som kan vara antingen villkor (if-else, switch), iteration (while, do-while, for) eller avbrott (break, continue och return). Till Javaspråket hör ett stort antal klasser organiserade i bibliotek eller paket med klasser. Detta är klasser som utvidgar själva språket, stödklasser för datastrukturer, filoch I/O-hantering, nätverkskommunikation och Internet, Applets och användargränssnitt. Den virtuella Javamaskinen är ett plattformsspecifikt program som läser kompilerad bytekod och exekverar den på aktuell plattform. För att uppnå maximal prestanda kan olika kompileringstekniker tillämpas. Just-In-Time kompilator som översätter Javabytekoden ”i flykten” till maskinkod på aktuell plattform. Det finns också rena kompilatorer som kompilerar Javakällkod (eller bytekod) till en exekverbar fil för aktuell plattform. Den senaste tekniken är en s.k. ”HotSpot” virtuell maskin som utför adaptiv optimering av koden under exekveringen av programmet. Det optimerar enbart de kritiska ställena i koden, vilket medför att mer avancerad optimering kan utföras. Robert Jonsson ITM Östersund Sida 15 Lektion 2 Java I, 5 poäng Vi börjar nu titta på det mest väsentliga i ett objektorienterat språk som Java nämligen klasser. Som nämnts beskriver en klass en objekttyp, d.v.s. hur objekt av klassen ska representeras och vilka metoder de ska stödja. En klass beskriver objektens egenskaper och beteende, d.v.s. vilka instansvariabler och metoder objektet har. Instansvariabler används för att lagra information om objektet och metoderna används för att behandla denna information. När klassen väl är skriven fungerar den som en ritning för de objekt som ska skapas av klassen. Utifrån klassbeskrivningen skapas sen olika objekt. Man brukar säga att man skapar instanser (förekomster) av klassen, objekten instansieras. Robert Jonsson ITM Östersund Sida 16 Lektion 2 Java I, 5 poäng En klass börjar alltid med en klassdeklaration, vilken i sin tur börjar med nyckel ordet class följt av namnet på klassen. Vi har redan nu provat på att skapa ett flertal olika klasser och alla har börjat med en klassdeklaration. Därefter ramas klassens innehåll in med en påbörjande vänsterklammer { och en avslutande högerklammer }. Det som ingår i klassen kallas för medlemmar, och en medlem kan antingen vara ett instansvariabel eller en metod. Hittills har våra klasser endast innehållit en enda medlem, metoden main(). En klassdeklaration kan också innehålla en specifikation om klassen ska ärva en annan klass och ha speciella modifierare för att ge klassen andra speciella egenskaper. Robert Jonsson ITM Östersund Sida 17 Lektion 2 Java I, 5 poäng Vi ska nu testa på att skriva en första klass enligt specifikationen ovan. Det brukar vara vanligt att med UML visa hur klassens specifikation ser ut (vilka instansvariabler och metoder den innehåller). Enligt beskrivningen ska klassen representera en punkt och namnet på klassen väljs lämpligen till Punkt (alla klasser ska ha STOR första bokstav i varje ord). I ett s.k. klassdiagram (UML) placerar vi namnet på klassen överst. För att kunna lagra information om punktens koordinater behöver vi två variabler. Eftersom dessa tillhör klassen blir det klassens instansvariabler. Vi döper dem till xkord respektive ykord och väljer datatypen int (heltal). Vi ska kunna sätta nya värden på koordinaterna och måste då ha metoder för detta (instansvariablerna används för att lagra information om objektet och metoderna används för att behandla denna information). Vi behöver därför metoderna setx() och sety(). För att kunna skriva ut koordinaterna använder vi metoden print(). Observera att namnen på instansvariablerna och metoderna hade kunnat väljas till något annat. Vi har nu klart för oss hur klassen ska se ut. Återstår nu ”bara” att skriva koden. Robert Jonsson ITM Östersund Sida 18 Lektion 2 Java I, 5 poäng I denna klass finns det nu fem medlemmar: medlemsmetoderna setX(), setY() och print(), samt instansvariablerna xkord och ykord. Ordningen på medlemmarna spelar ingen roll, de hade kunnat stå i en annan ordning. Vi hade till exempel kunnat börja med att skriva medlemsmetoden setX() och därefter våra instansvariabler. Det viktiga är att vi skriver dem inom klassdeklarationen. Dock är det vanligt att samla alla instansvariabler överst och därefter metoderna. På de följande sidorna tittar vi närmare på de olika delarna i klassen. Robert Jonsson ITM Östersund Sida 19 Lektion 2 Java I, 5 poäng Varje klass börjar som vi redan nämnt med en klassdeklaration. Klassdeklarationen ger klassens dess namn, samt specificerar hur klassen kan användas, t.ex. vilka andra klasser/objket som kommer att få tillgång till klassen och om klassen i fråga ärver egenskaper från någon annan klass. En klassdeklaration består av en eller flera åtkomstmodifierare, därefter nyckelordet class, följt av det namn du vill ge din klass. Sist kan man ange om man vill att klassen ska ärva eller implementera någon annan klass. Både klassmodifierare och eventuella arv behöver inte anges. Om de utesluts kommer Java att använda default-värden. Java kommer då att begränsa tillgången till klassen något samt att låta den ärva från klassen Object (som är en Superklass till alla klasser i Java). Det normala är att använda public som åtkomstmodifierare för klasser. Detta innebär att vilka andra klasser som helst kan skapa ett objekt av klassen. Observera att vi aldrig kan använda åtkomstmodifieraren private i en klassdeklaration. I exemplen har jag skrivit en klass, Privat.java, där jag använd private i klassdeklarationen. Prova att kompilera och köra denna klass för att se vad som händer. Robert Jonsson ITM Östersund Sida 20 Lektion 2 Java I, 5 poäng En instansvariabel är en datavariabel som lagras i ett objekt. Samtliga instansvariabler i ett objekt representerar tillsammans objektets information. En person kan t.ex. representeras med instansvariabler för personens namn, ålder och personnummer och en punkt kan representeras med x- och y-koordinat. I objektorienterad programmering försöker man kapsla in ett objekts information och göra det ”osynligt” för andra. Därför används normalt åtkomstmodifieraren private för instansvariabler. Detta innebär att inga andra klasser kan få en direkt åtkomst till det värde som finns lagrat i en instansvariabel. Det är endast den egna klassen som kan komma åt de värden som finns lagrade i instansvariabler. Om andra klasser behöver tillgång till vissa instansvariabler är det bättre att tillhandahålla speciella set- och get-metoder än att deklarera instansvariabler som publikt (därför har vi i klassen Punkt satt private för instansvariablerna för att ”gömma” information om koordinaterna från andra klasser, men har publika set()metoder för att ändra värdet på instansvariablerna). Instansvariablerna behöver inte deklareras på något speciellt ställe utan kan stå före, mellan eller efter metoderna. Men normalt samlar man alla instansvariabler på ett och samma ställe för att göra koden mer lätt läst (och då vanligtvis överst i koden direkt efter/under klassdeklarationen). Robert Jonsson ITM Östersund Sida 21 Lektion 2 Java I, 5 poäng I vårt exempel deklareras två instansvariabler, xkord och ykord. Deklarationerna består av en åtkomstmodifierare (private), en datatyp (int), samt ett namn (xkord). Åtkomstmodifieraren specificerar om objekt av andra klasser ska ha tillgång till denna medlem. I detta fall är instansvariabeln deklarerat som privat och är därför inte tillgänglig för objekt av andra klasser. Datatypen specificerar vilka värden som kan lagras i instansvariabeln och vilka operationer som kan utföras på den. xkord och ykord är av typen int, som endast kan lagra heltal. Instansvariabelnamnet är det namn vi döper typen till. Det är detta namn vi sen använder för att referera eller identifiera vår variabel med, och som vi använder för att utföra operationerna med. Som datatyp kan anges primitiva typer som int, char, double, men också namn på andra klasser som Punkt. Robert Jonsson ITM Östersund Sida 22 Lektion 2 Java I, 5 poäng Metoderna i en klass beskriver de tjänster som klassen tillhandahåller. Metoderna är den del i en klass som innehåller den aktiva delen (där något egentligt arbete utförs). Metoder innehåller den kod som verkligen utför någonting. All aktiv kod som exekveras finns i metoder i klassen. Det finns inget sätt att deklarera metoder utanför en klass (som t.ex. i C++). Att utföra den kod som finns i en metod sker via s.k. metodanrop. Metoder är de delar i en klass som innehåller exekverbara satser. En metod är ett namngivet block av kod som utför en viss uppgift. Genom att associera ett namn till metoden kan detta namn användas för att exekvera satserna i metoden via s.k. metodanrop. De klasser vi hittills har skrivit har endast innehållit main()-metoden. Denna kan inte anropas av andra klasser utan anropas av den virtuella maskinen när klassen först startas. Robert Jonsson ITM Östersund Sida 23 Lektion 2 Java I, 5 poäng Vår exempelklass (Punkt) har tre metoder: setX() setY() och print(). En metod definieras med en eller flera modifierare. Normalt deklareras alltid en metod som public eftersom vi vill att andra klasser (objekt) ska kunna anropa metoden för att exekvera den kod som finns skriven där. T.ex. vill vi att andra klasser ska kunna anropa setx()och setY() för att kunna ändra punktens koordinater, eller print() för att kunna skriva ut information om punkten. Alla metoder har en returtyp som anger vad som returneras från metoden när alla kod i den har exekverats. Som returtyp kan man ange någon av de primitiva datatyperna (int, char, double etc), men man kan även returnera andra objket från en klass. Anger man nyckelordet void returnerar metoden inget värde. Main()-metoden deklareras alltid som void och den returnerar därför inget. Namnet på metoden kan man själv välja. Samma regler som för namn på instansvariabler och variabler gäller när vi namnger en metod. Tillskillnad från klasser där förstabokstaven i varje ord har STOR bokstav, så har vi liten bokstav på första ordet i metoden. Eventuellt resterande ord i metodnamnet får STOR bokstav. En metod kan eventuellt ha en eller flera parametrar, som anges inom parenteser (i vårt fall har metoden setX en parameter). Parametrar används för att skicka data till den metod som anropas. I vårt fall ovan måste vi skicka in det nya värdet på x- eller y-koordinaten när metoderna setX eller setY anropas. Slutligen kommer kodsatserna för metoden inom matchande klamrar (precis som i fallet med klassdeklarationen). Definitionen av metoden kan läggas vart som helst inom klassdeklarationen, men läggs normalt efter deklarationen av instansvariabler. Vi har nu gått igenom alla deklarationer i klassen (dock inte den kod metoderna innehåller). I exemplen som följer med lektionen finner du klassen Punkt.java. Prova att kompilera och köra den. Som du kommer att se får vi ett felmeddelande när vi kör klassen. Vad är det för felmeddelande och varför får vi ett fel? Robert Jonsson ITM Östersund Sida 24 Lektion 2 Java I, 5 poäng Som du märkte när du försökte köra klassen Person så fick du ett felmeddelande. Anledningen är att klassen saknar en main()-metod och är därför ingen körbar Javaapplikation. Kom ihåg att alla applikationer i Java måste innehålla en main()metod. För att vi ska kunna använda klassen Person måste vi skapa ett objekt av klassen. När klassen är definierad (dess instansvariabler och metoder är skapade) så är det sedan möjligt att skapa objekt av klassen. Som nämnts så är objekt en instans (förekomst) av klassen, och fungerar som en variabel av den typ som klassen definierat. Ett objekt av klassen kan skapas var som helst i en applikation där klassen är åtkomligt (inom klassen eller i andra klassers metoder). När objektet är skapat är det möjligt att referera och använda de metoder och instansvariabler som deklarerats som tillgängliga (publika) i klassen. De metoder och instansvariabler som inte deklarerats som tillgängliga är skyddade och dolda inuti objektet och är inte tillgängliga för andra klasser och objekt. Robert Jonsson ITM Östersund Sida 25 Lektion 2 Java I, 5 poäng Ett objekt skapas genom att använda operatorn new. Det är endast med new vi kan skapa objekt i Java. För att skapa ett objekt av klassen Punkt skriver vi: Punkt p1 = new Punkt(); Här sker både en deklaration och en tilldelning på samma gång. Vi deklarerar variabeln p1 till att vara av (den egendefinierade) typen Punkt. I p1 kan vi med andra ord lagra objekt som är skapade av klassen Punkt. Vi tilldelar även variabeln p1 ett nytt objekt av klassen Punkt. Efter operatorn new skrivs namnet på klassen och inom parenteser eventuella argument till klassens konstruktormetod (konstruktorn beskrivs senare). new kommer då att skapa ett objekt av angiven klass genom att reservera utrymme för objektet i minnet och sen initiera det (bl.a. skapa instansvariabler och eventuellt tilldela dessa värden). Från new-operatorn returneras sedan en referens till detta objekt. Referensen i exemplet tas emot och lagras i variabeln p1 (brukar ibland kallas referensvariabel). Se figuren till höger i bilden. Variabeln p1 är deklarerad att vara av typen Punkt, vilket ska läsas som att den är avsedd att hålla en referens till ett Punkt-objekt. Via referensen p1 (namnet på variabeln) kommer man åt de instansvariabler och metoder som det refererade objektet har och som är deklarerad att vara publika. I Java hanteras objekt alltid via objektreferenser, vilket leder till att man kan höra både termen ”objektet p1” och ”objektreferensen p1” när man talar om variabeln i fråga. Jag kommer att använda objektet p1 när det pratas om variabler/instansvariabler som är tänkt att lagra ett objekt. Skillnaden mellan instansvariabler/variabler som lagrar data av en primitiv typ och de som lagrar ett objekt är att i variabeln för primitiva typer lagras värdet, medan det för ett objekt lagras en referens (adress till objektet i minnet) till objektet. Observera att detta kan liknas med pekare i C++, men riktigt så är inte fallet. Robert Jonsson ITM Östersund Sida 26 Lektion 2 Java I, 5 poäng När objektet skapats är det möjligt att anropa objektets publika metoder. Att anropa en metod på ett objekt görs genom punktnotation där man skriver objektets namn följt av en punkt och därefter metodnamnet och metodens eventuella parametervärden (data som ska skickas till metoden för bearbetning) inom parenteser. Programsatsen avslutas som alltid med ett semikolon (vilket inte klass- och metoddeklarationer gör). Det sätt vi kommer att prova de klasser vi skriver i denna kurs är genom att skapa en, vad jag kallar, testklass. I denna testklass skapar vi ett objekt av den ”riktiga” klassen och provar att anropa metoder m.m. I UML kan vi ”rita” detta enligt bilden ovan. Ta nu och öppna klassen PunkTest.java som finns i exemplen. Där provar vi att skapa objekt av klassen Punkt och anropar dess metoder. Kompilera och kör klassen. Det är även möjligt att i klassen Punkt ha en main()-metod och då inte behöva en testklass. Detta demonstreras i Punkt2.java. Givetvis kan vi från en och samma klass skapa många objekt. Detta visas i PunktTest2.java. Robert Jonsson ITM Östersund Sida 27 Lektion 2 Java I, 5 poäng Instansvariabler som deklareras på klassnivå i Java har en räckvidd som sträcker sig genom hela klassen där den är deklarerad. D.v.s. vi kan i klassens alla metoder (oavsett om de är publika eller privata) använda klassens instansvariabler. Jag har hittills enbart deklarerat en instansvariabel, inte tilldelat instansvaribeln något värde i samband med deklarationen. Vad tror du händer om vi i en metod försöker att skriva ut värdet på skärmen, innan vi gett instansvariabeln ett värde? Prova att i PunktTest.java ändra koden så att metoden print() anropas före setX() och setY(). Vad är det som skrivs ut? Robert Jonsson ITM Östersund Sida 28 Lektion 2 Java I, 5 poäng I samband med att instansvariablerna deklareras är det också möjligt att tilldela dem värden. T.ex: private inte xkord = 100; Initieringen av instansvariablerna kommer att göras i samma ordning som de står uppräknade i klassen, och det är inte tillåtet att i en initiering använda en instansvariabel som är deklarerat efter den aktuella instansvariabeln. Alla instansvariabler intitieras innan en eventuell konstruktor körs (mer om konstruktorn senare). Om ingen initiering ges kommer primitiva variabler att sättas till värdet 0 och objekt referenser till att sättas till null. Instansvariabler av typen boolean kommer att tilldelas värdet false. Robert Jonsson ITM Östersund Sida 29 Lektion 2 Java I, 5 poäng OBS! Deklarationen av z1 hade inte kunnat stå före deklarationen av x och y! Robert Jonsson ITM Östersund Sida 30 Lektion 2 Java I, 5 poäng Det finns fyra stycken möjliga åtkomstformer till instansvariabler och metoder: Publik åtkomst (public) Skyddad åtkomst (protected) Privat åtkomst (private) Och paket åtkomst. På varje instansvariabel och metod kan åtkomst specificeras genom att som modifierare använda något av nyckelorden public, protected eller private. Om ingenting anges erhålls den fjärde formen, paketåtkomst. Robert Jonsson ITM Östersund Sida 31 Lektion 2 Java I, 5 poäng En medlem (instansvariabel eller metod) som har publik åtkomst är tillgänglig för ”alla”, d.v.s. anropningsbar både inom klassen och för de som skapat objekt av klassen. Normalt är de flesta metoder publika. Även klassen kan deklareras med en åtkomstmodifierare. En klass som deklareras som public är åtkomlig i paket utanför det paket som den är definierad i. Mer om paket och detta i senare lektioner. Robert Jonsson ITM Östersund Sida 32 Lektion 2 Java I, 5 poäng Skyddad åtkomst deklareras med modifieraren protected, och innebär att metoden och/eller instansvariablen: Är åtkomlig inom klassen, d.v.s. för andra metoder inom klassen. Är åtkomlig för subklasser, d.v.s. de som ärvt ifrån klassen. Är åtkomlig för andra metoder inom samma paket som klassen. Inte är åtkomlig för dem som skapat objekt av klassen. Robert Jonsson ITM Östersund Sida 33 Lektion 2 Java I, 5 poäng Ett instansvariabel eller metod som är deklarerats som privat är garanterat endast tillgänglig inom sin egen klass. De är inte åtkomliga ifrån de som skapat objekt av klassen, inte åtkomliga från subklasser, och inte åtkomliga från andra klasser inom samma paket. Normalt bör instansvariabler vara privata eftersom de data som en klass har inte bör vara synligt utåt, utan bara kommas åt indirekt via klassens metoder. Det kan ibland också finnas privata metoder som är interna hjälpmetoder för andra (publika) metoder. Robert Jonsson ITM Östersund Sida 34 Lektion 2 Java I, 5 poäng Om ingen av nyckelorden public, protected, private anges framför en instansvariabel eller en metod får den som default paketåtkomst. Det finns inget nyckelord för att uttryckligen ange paketåtkomst. Med paketåtkomst gäller följande regler för dessa klassmedlemmar: De är åtkomliga inom det paket i vilken klassen ingår. De är inte åtkomliga utanför det paket i vilket klassen ingår. Generellt gäller att man inte bör använda sig av paketåtkomst utan alltid skriva om en medlem ska vara public, protected eller private. Robert Jonsson ITM Östersund Sida 35 Lektion 2 Java I, 5 poäng Vi ska nu skapa ytterligare en klass och testklasser. Enligt beskrivningen ovan ska klassen denna gång representera en person och namnet på klassen väljs därför till Person. För att kunna lagra information om personens namn och personnummer behöver vi två instansvariabler. Vi döper dem till namn och persnr och väljer datatypen String (sträng eftersom det är text som ska lagras). Instansvariablerna ska vi sätta till private och i UML-klassdiagram visar vi att åtkomstmodifieraren ska vara private genom att sätta ett minustecken (-) framför namnet. Vi ska kunna sätta nya värden på personens namn och personnummer och vi måste skapa två metoder för detta (instansvariabler används för att lagra information om objektet och metoderna används för att behandla denna information). Vi behöver därför metoderna setNamn() och setPersnr(). För att kunna skriva ut information om personen använder vi återigen en metod med namnet print(). Eftersom vi vill att andra klasser/objekt ska kunna anropa våra metoder måste vi använda public som åtkomstmodifierare. I UML markerar vi detta genom att sätta ut ett plustecken (+) framför namnet. För att se hur koden för denna klass skrivs kan du ta en titt på Person.java i exemplen. Till denna har jag skapat tre testklasser. Den första, PersonTest1, skapar ett objekt på vanligt sätt som vi hittills gjort. I PersonTest2 använder vi Bufferedreader för att fråga användaren efter vilket namn och personnummer objektet ska ha. I PersonTest3 använder vi dialogrutor för att fråga efter objektets data. Robert Jonsson ITM Östersund Sida 36