Labb 1: Intro till Java utan objekt
Förberedelser
Början av labben baseras på FÖ 1 (introduktion, Java för Python-programmerare). Senare delar baseras även på FÖ 2. Du får börja tidigare om du vill, men var i så fall beredd på att uppgifterna kan baseras på sådant vi ännu inte har diskuterat.
Syfte: Java, utvecklingsmiljö
Här ger vi ett första smakprov på programmering i Java och programmering med explicita typer. Vi börjar med enklare uppgifter utan objektorientering, för att ge en stabil grund i det som skiljer sig från t.ex. Python. Detta är ett direkt önskemål från tidigare studenter.
Vi vill också introducera utvecklingsmiljön, så ni hittar funktionalitet som tidigare studenter har haft mycket nytta av. Det sparar tid i det långa loppet.
Labbarna har ofta detaljerade instruktioner. Det är ett medvetet grepp för att ni ska få lära genom att göra, istället för att ni får höra allt teoretiskt på föreläsningar och sedan omedelbart förväntas kunna applicera kunskaperna i praktiken.
Vägen är målet!
Vill kursledningen desperat ha 120 nästan identiska program som hälsar världen och som summerar, multiplicerar och hittar primtal? Nej, givetvis inte. Slutresultatet är alltså inte en uppsättning små program, utan består av kunskap, förståelse och färdigheter. Programmen finns där för att du ska få undersöka vägen som leder till en viss enkel funktionalitet.
Därför ger vi en hel del information som egentligen inte behövs för att man ska skriva rätt kod: Diskussioner av sammanhang, text som visar varför man gör på ett visst sätt, som generaliserar från nuet till det du kommer att ha nytta av i framtiden. Ta dig tid att läsa och reflektera!
Info: Deadlines och krav
Labben examineras via demonstration utan kodinlämning senast vid deadline.
Utvecklingsmiljö: IntelliJ IDEA
Syfte: Få hjälp med tråkiga rutinuppgifter!
Syfte: Erfarenhet av flera miljöer!
Precis som ni behöver lära er flera programmeringsspråk behöver ni också använda flera utvecklingsmiljöer. Detta är en anledning till att vi inte fortsätter med Eclipse, som många av er säkert har använt förut. Istället använder vi åtminstone i labbarna alltid IntelliJ IDEA, som finns både i kommersiell variant och som gratis open source-version (Community Edition) som kan användas hemma.
Från och med miniprojektet får man själv välja miljö – men tidigare år har många Eclipse-vana studenter sett fördelar i IDEA, varit glada för att få testa något nytt, och fortsatt med detta under hela kursen.
Använd gärna referenskortet till hjälp.
Bakgrund: Konfigurera och starta IDEA
IDEA är redan installerat på labbdatorerna, men ni behöver ändå installera vissa konfigurationsfiler på det egna kontot.
Att göra: Installera och starta IDEA
Installera konfigurationsfiler med följande kommando:
/home/TDDD78/bin/installidea.sh
IDEA startas sedan med följande kommando:
/home/TDDD78/bin/idea15.sh
IDEA kräver en licens som delas ut av en licensserver. När du startar första gången får du därför upp en dialog med namnet "IntelliJ IDEA License Activation". Gör så här:
Välj Activate your license with: License server.
Gå till License server address och skriv/klistra in http://appserv.ida.liu.se:4900/
Tryck OK
Om du får en dialogruta med namnet "Platform and Plugin Updates" väljer du "Ignore This Update".
Det kan hända att du får en del varningar vid starten. Följande varningar kan du strunta i:
Note: Storing IDEA's temporary files on the local disk (good!) [YourKit Java Profiler 2014 build 14112] Log file: /home/.../.yjp/log/IntelliJIdea15-33899.log /sw/intelliJ-15.0.3-EAP/bin/idea.properties: 'idea.max.intellisense.filesize' already defined /sw/intelliJ-15.0.3-EAP/bin/idea.properties: 'idea.cycle.buffer.size' already defined /sw/intelliJ-15.0.3-EAP/bin/idea.properties: 'idea.no.launcher' already defined /sw/intelliJ-15.0.3-EAP/bin/idea.properties: 'idea.dynamic.classpath' already defined /sw/intelliJ-15.0.3-EAP/bin/idea.properties: 'idea.popup.weight' already defined /sw/intelliJ-15.0.3-EAP/bin/idea.properties: 'idea.use.default.antialiasing.in.editor' already defined /sw/intelliJ-15.0.3-EAP/bin/idea.properties: 'sun.java2d.noddraw' already defined /sw/intelliJ-15.0.3-EAP/bin/idea.properties: 'sun.java2d.d3d' already defined /sw/intelliJ-15.0.3-EAP/bin/idea.properties: 'swing.bufferPerWindow' already defined /sw/intelliJ-15.0.3-EAP/bin/idea.properties: 'sun.java2d.pmoffscreen' already defined /sw/intelliJ-15.0.3-EAP/bin/idea.properties: 'idea.xdebug.key' already defined /sw/intelliJ-15.0.3-EAP/bin/idea.properties: 'idea.fatal.error.notification' already defined log4j:WARN No appenders could be found for logger (io.netty.util.internal.logging.InternalLoggerFactory). log4j:WARN Please initialize the log4j system properly. log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Info: Vad händer?
Efter en stund kommer IDEAs startup-skärm att visas. Den ser ungefär ut så här (lite beroende på operativsystem och version):

Att göra: Skapa IDEA-projekt
Nu skall vi skapa ett första IDEA-projekt som ska innehålla filerna för labb 1.
Välj "Create New Project" som öppnar följande dialog:
Se till att "Java" är valt i listan till vänster. IDEA har stöd för många andra projekttyper, men nu ska vi programmera i ren Java. Inga "additional libraries and frameworks" behöver väljas.
-
IDEA behöver hitta en SDK, Software Development Kit, som innehåller utvecklingsverktyg för det språk vi har valt. I nuläget finns ingen Java SDK konfigurerad, så välj "New..." och sedan "JDK", alltså Java Development Kit.
Som standard kan det hända att IDEA tittar i mappen /usr/lib/jvm. Där ligger visserligen några Java-versioner, men inte den vi ska använda, som är nyare. Därför behöver du navigera till mappen /sw/jdk8. Den här mappen pekar automatiskt ut den senaste versionen av JDK 1.8, t.ex. jdk1.8.0_60.
Tryck sedan på OK följt av Next.
Vi ska inte välja "Create project from template", så tryck Next.
-
Ge projektet ett namn vid Project name, t.ex. "TDDD78-lab1".
-
Tala om för IDEA var projektfilerna skall lagras vid Project location (alla kodfiler kommer att hamna här). Den plats som IDEA föreslår är en katalog som heter "IdeaProjects" i hemkatalogen vilket kanske inte är optimalt om du redan har en katalog för TDDD78.
-
Expandera "More Settings" och säkerställ att Project format är ".ipr (file based)".
-
Ett IDEA-projekt består av en eller flera moduler, där varje modul kan användas av flera projekt. Vi kommer enbart att ha en modul per projekt. Ändra Module name till "lab1". Behåll resten av inställningarna som de är.
-
Tryck på "Finish".
Info: Vad händer?
Nu har ett IDEA-projekt skapats. Det tar en stund för IDEA att starta upp eftersom den första gången behöver indexera alla de klasser och funktioner som finns tillgängliga för er som standard i Java. Indexeringen kan ta runt en halv minut och sker i bakgrunden. Under tiden kan man börja arbeta med de flesta av IDEAs funktioner.

IDEA varnar för att "external file changes sync may be slow". Detta beror på att er egen källkod ligger på en nätverksdisk och betyder helt enkelt att om ni gör ändringar i filerna utanför IDEA kan det ta något längre tid innan IDEA inser detta och läser om filen. Rutan kan stängas (peka på den så ser ni ett kryss att klicka på).
Projektvyn kan vara dold när IDEA startas. Då öppnas den med Alt+1, genom att trycka på 1: Project uppe till vänster, eller genom att välja View | Tool Windows | Project. Du kan expandera vyn över ditt projekt för att se alla relevanta mappar och filer inklusive externa bibliotek, för närvarande bara JDK.
Övning 1: Ett första program
Syfte: Komma igång!
Syftet med denna övning är att så snabbt som möjligt skriva och köra ett första Java-program. Vi börjar därför med en enkel "Hello World".
Bakgrund: Skapa paket och klass
Hela labb 1 kommer att fokusera på procedurell programmering, där vi
inte skapar egna objekt. Eftersom Java är objektorienterat måste man
dock alltid "kapsla in" sin kod i en klass. Vi vill därför börja med
att skapa klassen Exercise1
.
Men var ska klassen skapas? Java använder hierarkiska
paket, på engelska packages, för att organisera klasser i
större enheter och för att undvika namnkollisioner. Alla klasser i
den här labben ska läggas i
paketet se.liu.ida.dinadress.tddd78.lab1
, där
dinadress är din e-postadress (noone123).
En katalogstruktur matchande paketnamnet kommer att skapas, där varje punkt i namnet anger en ny katalognivå. IDEA kan dock visa paket ihoptryckta så att "tomma mellannivåer" göms i projektvyn. Detta och andra inställningar sköts via kugghjulet överst i vyn.
Att göra: Skapa paket och klass
Skapa ett paket genom att högerklicka på src som ligger
under modulen lab1 i projektbrowserns
hierarki. Att src är blå indikerar att den är en
källkodsmapp. Välj därefter New |
Package och skapa
paketet se.liu.ida.dinadress.tddd78.lab1
.
Skapa sedan klassen Exercise1
genom att högerklicka
på paketet (lab1) och välja New | Java
Class för att skapa klassen. Du skriver bara
in Exercise1
, men det fulla namnet för klassen
inklusive paket blir
se.liu.ida.dinadress.tddd78.lab1.Exercise1
. Klassen
skapas med attributet public
vilket betyder att kod i
alla klasser kan komma åt den. Mer info om åtkomsträttigheter
kommer under föreläsningarna.
Info: Varför så konstiga färger?
Nu har "skelettet" till en klass skapats, men en del färger kan se
lite underliga ut. Klassnamnet "Exercise1
" är till exempel grönt
på svart bakgrund. Varför då?
Pekar du på färgläggningen får du svaret: IDEA har upptäckt att du har en klass som aldrig används, och att den dessutom är tom. Detta är två av hundratals olika varningar som IDEA kan ge. Ibland, som nu, kommer en varning helt enkelt för att vi inte har hunnit skriva mer än. Ibland kan det hända att vi har använt en klass tidigare men slutat med det, och då är varningen bra att ha. Ibland upptäcks allvarligare fel där varningarna kan vara livräddare. Och ibland är varningarna helt enkelt fel, eftersom ett program som IDEA aldrig kan ha full förståelse av hur koden är tänkt att användas. "I genomsnitt" brukar varningarna i alla fall kunna ge väldigt mycket hjälp med vissa typer av problem.
Bakgrund: Skapa huvudmetod
I ett enkelt "Hello World"-program behöver vi inga objekt, men vi
behöver veta vilken kod som körs när ett visst program
startas. Python kör all kod som är på "toppnivå" i den Python-fil
man anger. I Java anger man istället en klass, och en
speciell main
-metod (funktion) i den klassen startas.
Metoden måste ha en specifik signatur (parametrar, returvärden,
...).
Att göra: Skapa huvudmetod
Skriv in koden för ett litet Hello World-exempel enligt bilden nedan.
Man kan med fördel använda IDEAs Live Template-funktion
genom att ställa markören i klassen (inom måsvingarna),
välja Code | Insert Live Template:
Ctrl-J , och sedan välja main() method
declaration
. Ett ännu snabbare alternativ är att
skriva psvm
och trycka Ctrl-J. Det samma
gäller System.out.println();
som kan fås
via sout
och Ctrl-J.
Som synes fick "Exercise1
" nu en annan färg, eftersom vissa
varningar försvann (klassen är inte tom och den kan användas genom
att main()
anropas) medan andra tillkom. I labb 1 kan ni ignorera
de flesta varningarna eftersom de gäller sådant vi ännu inte har
diskuterat.
Bakgrund: Köra programmet
Vi ska nu köra programmet. Till skillnad från Python har Java först ett separat kompileringssteg där de .java-filer som innehåller källkoden ska översättas till ett internt bytekod-format, som då sparas i form av .class-filer. Hade vi arbetat från kommandoraden hade vi behövt använda verktyget javac för att kompilera filen och sedan köra "java se.liu.ida.dinadress.tddd78.lab1.Exercise1". Inifrån IDEA är det enklare.
Att göra: Köra programmet
Högerklicka någonstans i den öppna filen och välj Run Exercise1.main(). Detta kompilerar automatiskt alla okompilerade filer, verifierar att inga kompileringsfel uppstod, och startar det program vi högerklickade. Du bör se utskriften i IDEAs körfönster:
Då man kört programmet sparas en körkonfiguration. Man kan välja bland körkonfigurationer och köra dem enkelt via knappmenyn. Om det inte syns en knappmeny i IDEA kan denna tas fram genom View | Toolbar.
Sammanfattning
Vi har nu sett hur man skapar nya paket, skapar nya klasser, skriver ett enkelt program, och startar det. Du behöver inte demonstrera förrän hela labb 1 är klar, men prata gärna med handledaren om du har frågor eller funderingar.
Info: Kompilering av projekt i IDEA
Som vi beskriv tidigare har Java till skillnad från språk som Python ett separat kompileringssteg som översätter källkoden (.java-fil) till en intern representation (.class-fil). IDEA kompilerar automatiskt innan ett program startas, men ibland vill man kompilera utan att köra, t.ex. för att dubbelkolla att inga syntaxfel finns kvar. Detta görs genom:
Build | Make Project Ctrl-F9, som kompilerar "det som behövs" men behåller det som inte ändrats
Build | Rebuild project, som kompilerar allt.
Övning 2: Kontrollstrukturer
Syfte: Bli bekant med kontrollstrukturer
Java har flera olika kontrollstrukturer för iteration (upprepning). Syftet med denna övning är att bli bekant med samtliga, för att kunna välja rätt variant vid rätt tillfälle.
Bakgrund: Skapa ny klass
I labb 1 skapar vi oftast en ny klass för varje uppgift.
Att göra: Skapa ny klass
Skapa klassen Exercise2
i det paket ni
skapade. Klassen kan skapas (a) på samma sätt som förut
genom att högerklicka på paketet (lab1) och välja New | Java
Class, eller (b)
genom att ställa markören på klassnamnet Exercise1
,
trycka F5 (clone) och svara på frågorna.
I det senare fallet behöver man också ta bort onödig kod i den nya klassen – eller kommentera bort den, om man tror att den snart ska användas igen. IDEA underlättar att kommentera bort kodavsnitt genom att man markerar dem och trycker CTRL-/.
Bakgrund: Summera tal med for
-loop
Här börjar vi med en vanlig for
-loop. Den här typen av loop används när man i förväg vet hur många
iterationer man vill utföra. Syntaxen är enligt följande:
for (initialization; condition; increment) {
statements
}
- Initialization
Här deklareras och initialiseras den räknare som vi använder. Som alltid i Java måste man ange variabelns typ vid deklarationen. Exempelvis:
int i = 1
omi
är en tidigare okänd variabel som vi vill använda som räknare. Vi använder ofta justi,j,k
som loopvariabel när vi har en kort loop och bara vill räkna antal gånger loopen körs eller t.ex. räkna positioner i en lista. Om variabeln betyder något mer, döper vi den så att den blir mer förståelig (x, pos, cardnum, ...
).- Condition
- Här beskrivs det villkor som måste vara uppfyllt för att iterera ett varv (till). Exempelvis:
i < 10
. - Increment
- Här anges hur räknaren skall uppdateras. Exempelvis:
i++
.
for (int i = 0; i < 10; i++) {
a = a + 10;
System.out.println("Nu har a värdet " + a);
}
När koden exekveras kommer först initialiseringskoden att köras. Det betyder att det kommer att finnas en int-variabel med
namn i och värde 0 i for
-loopen. Innan varje iteration av koden i loopen kontrolleras condition-villkoret. Om
det är uppfyllt exekveras koden i for
-loopen en gång. I det här fallet kommer värdet i variabeln a att ökas med
10. När koden i for
-loopen har exekverats anropas increment-koden som räknar upp räknaren. I det här fallet
ökar värdet på i med 1.
När exempelkoden körs är resultatet att a 10 gånger ökas med 10.
Notera att variabeln i
bara är känd i for-loopen och inte kan användas utanför. Det går däremot bra att
återanvända namnet till andra deklarationer.
Att göra: Summera tal med for
-loop
-
Skriv en funktion
sumFor(int min, int max)
som beräknar och returnerar summan av talen min,min+1,...,max med hjälp av enfor
-loop. -
Skapa en
main()
-funktion. Vi vill vänta lite till med att titta på hur man hämtar input från användaren. Vi börjar därför med att hårdkoda värden på min och max. Vi kommer senare att läsa in dessa från användaren. Definiera därförint min = 10;
och sätt på liknande sätt max till 20. Därefter skall programmet anropasumFor(min,max)
och skriva ut resultatet med hjälp av anropetSystem.out.println();
. Här lägger ni det som skall skrivas ut mellan parenteserna i funktionsanropet. -
Testkör!
Bakgrund: While, do-while
Java har också två andra loop-konstruktioner, while
och do-while
. De ser ut så här:
while (condition) {
statements
}
do {
statements
} while (condition);
Skillnaden mellan konstruktionerna är att do-while
alltid genomför loopen minst en gång, eftersom villkoret inte
testas förrän efteråt.
Dessa loop-konstruktioner är i grunden enklare
än for
-loopen, och kan passa bra när man har andra
typer av slutvillkor som inte direkt kopplas till en variabel som
räknas upp i varje steg. Exempel:
while (!queue.isEmpty()) {
// ... take element from queue ...
// ... do something with it ...
}
Att göra:
Eftersom konstruktionerna är så lika testar vi bara en av dem.
-
Skapa nu funktionen
sumWhile(...)
, som har samma parametrar och returvärden somsumFor()
men istället använder enwhile
-loop för att beräkna samma summa. -
Ändra
main()
så den även anroparsumWhile()
och skriver ut resultatet från båda anropen. Testkör programmet. Får du alltid samma summor?
Övning 3: Multiplikationstabell
Syfte: Återskapa!
Syftet med denna övning är arbeta vidare med kontrollstrukturer samt att återskapa något ni redan gjort i Python för att på det sättet tydligt se kontrasterna mellan språken. Den uppgift vi väljer är multiplikationstabellen (övning 102).
Bakgrund: Välja multiplikationstabell
Vi vill vänta lite till med att titta på hur man hämtar input från användaren. Vi börjar därför med att skapa ett program som skriver ut multiplikationstabellen för ett "hårdkodat" (fixerat) tal, t.ex. 5.
Istället för att bara använda en konstant som 5 "rakt av" är det
dock oftast bättre att ge den ett symboliskt namn, t.ex. genom att
deklarera en konstant TABELL
med värdet 5. Detta underlättar både kodförståelse ("TABELL" är lättare
att förstå än "5") och senare ändringar (lättare att ändra den
enda deklarationen än att ändra värdet "5" på massor av platser i
koden).
Att göra: Välja multiplikationstabell
Börja med att skapa klassen Exercise3 för övningen.
Deklarera en konstant som anger vilken multiplikationstabell vi skall skriva ut. Denna skall vara synlig endast i klassen och bör deklareras som följer, på toppnivå i klassen:
private final static int TABELL = 5;
Här stöter vi på Javas namngivningsstandard som säger att namn på denna typ av konstanter skall skrivas med stora bokstäver. Förklaringar till "final" och "static" kommer under föreläsningarna.
Info: Kodkomplettering och analys
När ni skriver kod i IDEA kommer ni att märka flera saker.
IDEA kommer att föreslå fortsättning på ord, något ni kan använda för att snabbt fylla i långa namn. Man väljer det alternativ som passar, t.ex. genom att skriva tills man lätt kan markera rätt namn och sedan trycka på TAB.
IDEA kommer också att föreslå och markera fel eller andra otydligheter i koden. Genom sin indexering vet IDEA alltid vad ett namn står för. På så vis kan den välja olika färgmarkeringar (syntax highlighting) för att indikera vad som är en klass, en statisk funktion mm. vilket underlättar läsning av kod.
Bakgrund: Utskrift
Nu är det dags att programmera iterationen och utskriften. Vi vill visa 1*TABELL, 2*TABELL, osv. upp till 12*TABELL.
I Java sätts strängar för utskrift samman genom konkatenering via
'+
'-operatorn. När man försöker "addera" ett tal och en sträng
förstår Java automatiskt att talen ska konverteras till strängar.
Man kan därför göra en utskrift enligt följande exempel, där i
är
en loopvariabel:
System.out.println(i + " * "+TABELL + " = " + i * TABELL);
Att göra: Utskrift
Konstruera en loop som skriver ut 5:ans multiplikationstabell.
Testkör sedan programmet på samma sätt som i tidigare övning.
Övning 4: Inmatning
Syfte: Testa inmatning från användaren!
Syftet med denna övning är att testa inmatning av värden från en användare.
Bakgrund: Inmatning
Vi vill nu låta användaren ange vilken multiplikationstabell som ska visas. Vi gör detta genom en utökning i samma klass som tidigare.
Java är inte lika anpassat för enkla kommandoradsprogram som Python. Därför är det faktiskt enklare att läsa in ett värde från en grafisk dialogruta än att ta inmatning från standard input!
Att göra: Inmatning
Utöka programmet från förra övningen för att läsa in ett värde från användaren. Detta måste göras i mainmetoden, innan loopen. Exempel:
String input =
JOptionPane.showInputDialog("Please input a value");
IDEA kommer att markera att den inte känner till klassen
JOptionPane
. Vi måste berätta var denna finns genom att antingen
trycka Alt + Enter när IDEA föreslår
detta, t.ex. då markören står framför raden, eller genom att
trycka på den röda glödlampan som dyker upp efter en stund till
vänster på raden och välja Import Class.
Värdet som läses in är en sträng, och måste konverteras till ett heltal (om möjligt). Exempel:
int tabell = Integer.parseInt(input);
Då detta är en lokal variabel namnges den inte med stor bokstav. Ändra därför från TABELL
till tabell
i den tidigare utskriften så att det nya värdet används.
Provkör!
Testa gärna att mata in en icke-numerisk sträng och se vad som händer. Vi lämnar som överkurs (frivilligt) att ta hand om felet som uppstår och låta användaren försöka på nytt.
Vi avslutar genom att lägga till inmatning även i övning 2 så att talen min och max som används i looparna läses in från användaren.
Övning 5: Felsökning med IDEAs hjälp
Syfte: Hitta syntaxfel
Denna övning syftar till att testa hur en programmeringsmiljö kan hjälpa till att hitta fel i koden.
Bakgrund: Syntaxfärgläggning
Det händer ofta när man skriver kod att något inte blir syntaktiskt korrekt. IDEA och många andra omgivningar hjälper till att lösa dessa problem innan man kompilerar genom syntaxfärgläggning. Vi demonstrerar detta med hjälp av fakultetsfunktionen.
Att göra: Syntaxfärgläggning
Börja med att precis som ovan skapa
klassen Exercise4
. I den, inuti klassdefinitionen
(mellan måsvingarna), klistrar du in följande felaktiga kod:
public static void main(String[] args) {
for (int i = 0; i < 10; i+1) {
system.out.println(i + "-fakultet: " + facrotial(i));
}
}
/**
* Calculates f! given f.
* @param f
* @return f!
*/
private factorial(int f) {
if (f = 0)
return 1;
}
int result = 1
for (int i = 1; i <= f; i+1) {
result *= i;
}
return result
}
IDEA kommer helt utan att kompilera koden att känna av och markera
en hel del fel. Det finns ett 10-tal fel varav ett är att
"static
"-kod i detta fall endast kan anropa annan
"static
"-kod. Din uppgift är att se till att den blir körbar och
skriver ut 0-fakultet till och med 10-fakultet.
Övning 6: Felsökning med debugger
Syfte: Börja testa debuggern
En debugger (avlusare) kan vara ett extremt användbart verktyg vid programmering. Syftet med denna övning är att introducera debuggern så tidigt som möjligt, för att förhoppningsvis spara mycket tid i senare skeden. Målet här är egentligen inte är att få ett fungerande program, utan att se hur man lagar ett trasigt program – därför får vi ta en del "omvägar"!
Bakgrund: Primtalsletare
För att även få lite mer programmeringsvana i Java skriver vi ett nytt program att avlusa. Detta program kommer att testa om ett tal är ett primtal.
När vi ska införa en väl avgränsad funktionalitet, som att testa primtal, bör den separeras ut för att förbättra modularitet och läsbarhet. Primtalstestet implementeras därför i en separat metod.
Att göra: Primtalsletare
Skapa en ny klass Exercise5
precis som tidigare.
Skapa en egen metod, public static boolean isPrime(int
number)
, vars ansvar är att testa om number
är
ett primtal.
I metoden lägger vi en for
-loop som går igenom alla tal 2,3,...,
inparameter-1 och ser om någon delar talet. I så fall returneras
false, annars true. Här behövs alltså en if
-sats inuti
for-loopen.
För att avgöra om ett tal delar inparametern använder vi följande felaktiga kod:
int rest = number / i;
if (rest == 0) {
// number är en jämn multipel av i
...
}
Bakgrund: Testkörning
Vi måste så klart testa för att se om det blir fel...
Att göra: Testkörning
Vi testkör nu funktionen. Låt main()
skriva ut resultatet
av isPrime(5)
. Starta klassen som vanligt. Svaret bör
bli true
om du har gjort rätt.
Modifiera programmet så att main()
istället skriver
ut isPrime(4)
. Det blir också true
vilket
ju är fel. Vi har alltså upptäckt ett fel i programmet.
Bakgrund: Påbörja avlusning
Vi kunde nu lägga till utskrifter för att ta reda på vad som egentligen händer i programmet, men det finns ett mer flexibelt sätt: Vi använder debuggern och ser till att programmet automatiskt stannar för inspektion vid en specifik brytpunkt (breakpoint).
Att göra: Påbörja avlusning
Sätt en brytpunkt på raden if (rest == 0)
genom att
vänsterklicka i listen till vänster om koden eller genom att stå
på raden och trycka CTRL-F8. En
brytpunkt indikeras genom en röd cirkel i listen samt att raden
blir rödmarkerad.
Starta programmet i debuggern genom att högerklicka och välja Debug Exercise5.main() (inte "Run"!). Det ger följande fönster:
Info: Vad har hänt?
Programmet har nu startats, men har automatiskt pausats då brytpunken nåtts. Detta visas genom att raden med brytpunkten får blå färg.
Notera att debug-fönstret visas under kodfönstret. I
debug-fönstret finns som standard tre mindre fönster. Det vänstra
visar vilken anropskedja som ledde oss till funktionen vi befinner
oss i körs – i det här fallet anropades den från main()
. Det
mittersta visar intressanta variabler och deras värden. Det högra
visar Watches som är variabler vi valt att hålla extra koll på
(för närvarande inga).
Bakgrund: Fortsätt avlusning
Nu är det dags att se vad som har hänt.
Att göra:
I debuggern ser vi att vi att number=4 och i=2. Vi ser i koden att vi försökte hitta resten vid division med i, och i debuggern att resten faktiskt blev 2, vilket inte är vad vi förväntat oss. 4 är ju jämnt delbart med 2!
Alltså måste något vara fel på den tidigare raden och mycket riktigt, / borde ju vara %. Ändra detta och testkör för att se att 4 inte längre klassas som ett primtal.
Avsluta debuggningen genom att trcka på den röda stoppknappen till vänster eller genom att trycka Ctrl-F2. Notera att debug-fönstret under koden stannar kvar. Ni kan antingen stänga det eller växla tillbaka till run-fönstret.
Info: Vad har vi uppnått?
Just i detta fallet ser man kanske inte omedelbart poängen. Men för mer komplicerade fel är debuggern enormt användbar, och under kursen kommer ni sannolikt att bekanta er mer med den.
En stor fördel är att ni omedelbart kan se värden på alla variabler. Ni kan också inspektera egenskaper hos objekt och följa hierarkier och pekare, något ni kommer att se mer av när vi kommer in på objektorientering. Att debugga kod utan att ta hjälp av en debugger är möjligt, så kallad "print-debuggning". Det går till så att man lägger in utskrifter av önskade variabler eller programflöden på strategiska ställen. Sedan kompilerar man och flyttar utskrifterna allt eftersom man får mer information om var man skall söka efter buggarna. Med hjälp av debuggern kan man istället få ut all information direkt och behöver inte kompilera och köra om programmet många gånger för att lokalisera en bugg. Det är därför väldigt tidsbesparande och ger även en inblick i hur koden faktiskt exekverar vilket leder till ökad förståelse.
Info: Vad mer kan man göra?
Run-menyn visar tillgängliga operationer att göra i debugläget. De vanligaste är:
- Step Over | F8 som exekverar en rad kod och går till nästa utan att gå in i funktionsanrop.
- Step Into | F7 som exekverar en rad kod och om raden innehåller ett funktionsanrop så hoppar den in i funktionen.
- Run to Cursor | Alt+F9 som exekverar tills den kommer till nuvarande markörposition. Detta är samma som att lägga till en brytpunkt, köra till den och sedan ta bort den igen, ett förfarande som ofta används.
- Run | F9 kör vidare till nästa brytpunkt.
Det finns många avancerade sätt att underlätta debuggning, t.ex. att köra tills ett givet villkor är sant, eller tills en given rad exekverats ett bestämt antal gånger. Ni kan hitta mer information via IDEAs hjälpsida.
Bakgrund: Hitta fler primtal
Vi avslutar den här övningen genom att skriva ut alla primtal under 100.
Att göra:
Gör en for-loop i main()
metoden. Iterera över talen
från 2 till 100 och skriv ut alla som klassificeras som primtal.
Om ni inte upptäckt det ännu så finns det
en fori
-mall för att snabbt få till en for-loop.
Använd den på samma sätt som psvm
och sout
.
Övning 7: Mer kontrollstrukturer
Syfte: Bli bekant med switch
-konstruktionen
Vi ska nu titta på switch
, som är ett alternativ
till if
i vissa fall.
Bakgrund: Switch
Vi ska nu testa en alternativ villkorssats: switch
.
Denna villkorssats testar inte godtyckliga villkor. Istället anger
man ett uttryck, och vilken gren man utför beror enbart på detta
uttrycks värde. Tidigare fungerade switch bara med uppräkningsbara typer, men
sedan Java 7 går det även att använda strängar. Vid
problem, dubbelkolla språknivån i File / Project
Structure / Projects / Project language level. Detta ska
sättas till "8 - Lambdas, type annotations etc.".
Mest stöd från IDEA fås vid användning av enumuttryck i switch-satsen eftersom IDEA då känner till vilka möjliga grenar som finns.
Att göra: Switch
-
Vi skall utöka koden i övning 2. Låt
main()
användaJOptionPane.showInputDialog
för att läsa in en textsträng som skall vara "for" eller "while".Låt switch-satsen anropa rätt summeringsfunktion beroende på denna input. Glöm inte
break
!Lägg även till en
default
-etikett i switch-satsen. Denna gren utförs om man inte skrev in något av de acceptabla valen och bör alltså skriva ut ett felmeddelande. -
Testa programmet!
Övning 8: Kortslutning av logiska operationer
Syfte
I Java finns två olika sätt att utföra de logiska operationerna and och or. Det är viktigt att man känner till skillnaderna i hur de fungerar. Om man gör fel kan det leda till att onödiga eller potentiellt farliga beräkningar görs.
Bakgrund
I Python finns operatorerna and och or. Båda dessa är kortslutande, vilket betyder att om resultatet är känt efter att första operanden beräknats kommer den andra aldrig att beräknas.
Konkret: Om Python behöver värdet av a and b börjar man med att räkna ut värdet på a. Om a är falskt vet man att a and b kommer att vara falskt oavsett vilket värde b råkar ha: Både false and false och false and true är ju falskt. Då struntar man helt enkelt i att beräkna b, för dess värde spelar ingen roll. På samma sätt fungerar a || b, men där slutar man beräkningarna om man har fått reda på att a är sant.
Samma funktionalitet finns i Java, med && istället för and och med || istället för or. Men det finns också en variant av vardera operator där bara ett tecken används istället: a & b och a | b. I dessa uttryck garanteras att både a och b räknas ut. I vissa fall kan detta vara användbart, men som vi ska illustrera nedan är det oftast den kortslutande varianten man vill ha.
Att göra
-
Skapa en ny klass, Exercise8, med en tom main-metod. Skriv sedan funktionen
askUser
som tar som inparameter en sträng som kommer att presenteras för användaren av programmet. Funktionen skall returnera sant om användaren svarar "ja", vilket åstadkoms genom att returnera det booleska värdet av följande uttryck:JOptionPane.showConfirmDialog(null, question, "", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION
-
Lägg till kod i main-metoden så att programmet ligger i en evig loop med hjälp t.ex. av en
while(true)
-loop.Nu skall man få möjligheten att avbryta, men bara om man är helt säker. Detta görs genom att ställa frågorna
askUser("Quit?")
ochaskUser("Really ?")
. Om båda svaren blir frånaskUser
blirtrue
skall programmet avslutas (return
från main-metoden) och annars ska det skriva ut att programmet fortsätter köra.Prova båda varianter nedan (en i taget!) och ge olika svar på frågorna:
if (askUser("Quit?") && askUser("Really?")) { ... } if (askUser("Quit?") & askUser("Really?")) { ... }
Vilken verkar bäst att använda i detta fall? Vad händer med respektive utan kortslutning?
Övning 9: Typning
Syfte
Typning i Java skiljer sig från hur det hanteras i Python. I Java sker typkontroll vid kompilering, eller i vårt fall ännu tidigare, så fort IDEA kan avgöra typerna.
Bakgrund
Vi skall återanvända ett exempel från Python-kursen, implementationen av Newtown-Raphsons klassiska metod, i det här fallet hårdkodad för att hitta kvadratroten av ett tal.
Att göra
- Skapa en ny klass, Exercise9, med en tom main-metod. Skriv sedan funktionen
findRoot
som tar som inparameter endouble
(vi vill ju räkna med decimaler) och returnerar endouble
. -
Givet att inparametern heter
number
skall funktionen först sätta variabelnx
till detta värde och sedan utförax = x - (x*x-number)/(2*x);
10 gånger innan den returnerar det slutliga värdet påx
. (Mer information om metoden finns här: Newton-Raphson.) - Lägg till inläsning av det värde vi vill hitta roten ur i main-metoden. Inläsning sker med hjälp av
String input = JOptionPane.showInputDialog("Please input a value");
Skriv sedan ut resultatet med
System.out.println("Roten ur "+input+"är "+findRoot(input));
- Vad händer när ni matar in koden? Går det att skicka input till findRoot? Hur kan IDEA veta att det inte går innan koden kompilerats?
-
Fixa koden genom att konvertera input till en double med hjälp av
Double.parseDouble
.
Info: Automatisk typkontroll
Att göra: Komplettering och typning
Det här är ett bra tillfälle att illustrera hur IDEAs funktion för komplettering blir mer kraftfull med hjälp av variabeltyper.
Testa i koden för övning 9 att ta bort parametern som skickas
till findRoot
, och ställ sedan markören i den tomma parentesen
precis som om du höll på att skriva den här koden just nu:
System.out.println("Roten ur "+input+"är "+findRoot([markör]));
När du trycker Ctrl-space (basic code completion) föreslår IDEA
allt som skulle kunna stå i den här positionen. Bläddra gärna i listan och
se vad som är möjligt att skriva här. Ett exempel är input
,
vilket kanske verkar underligt eftersom input är en sträng – men man
hade kunnat använda till exempel input.length()
,
vilket börjar med input
.
Men nu vet du kanske att du vill använda oss av en variabel som redan är av rätt typ. Då trycker du Ctrl-shift-space (type completion). IDEA föreslår då endast att man kompletterar med något som direkt matchar typen på den valda positionen. I detta fall minskas möjligheterna avsevärt. Du kan läsa mer här: auto completion.
Övning 10: Primitiva numeriska datatyper
Syfte
Vi skall nu titta lite mer på skillnader mellan de olika datatypter som används vid numeriska beräkningar. De vanligaste är int, long, float och double.
Bakgrund
Det är viktigt att känna till skillnaden mellan olika primitiva datatyper. Speciellt kan datatyper som används vid numeriska beräkningar ibland förväxlas. Vi skall nu titta lite extra på detta. Heltal lagras av de primitiva typerna int och long medan flyttal (decimaltal) lagras i float och double. Eftersom datatyperna har begränsade storlekar finns det gränser för vad som kan representeras av de olika typerna.
Att göra
Skapa en ny klass, Exercise10, med en tom main-metod. Vi skall nu lägga till kod till main-metoden.
-
Börja med att lägga till
int tal = 16777216;
. Vi skall nu konvertera denna till en float och sedan tillbaka till int för att se vad som händer. Lägg därför tillfloat decimaltal = tal;
. Lägg märke till att det inte innebär något problem för Java att automatiskt förstå att vi vill göra ett flyttal av vårt heltal. Eftersom det talområde som kan representeras av en float är större än motsvarande för en int accepteras detta av Java. Det sker en implicit typkonvertering, dvs vi behöver inte skriva något extra. -
Konvertera nu tillbaka genom att lägga till
int tillbaka = decimaltal;
. Vad händer?I det här fallet kan inte talområdet för en float rymmas i en int och Java kräver att man hanterar detta, t.ex. genom att göra en explicit typkonvertering (avrundning) genom att skriva
int tillbaka = (int)decimaltal;
-
Skriv ut tal, decimaltal och tillbaka och jämför vilka värden som skrivs ut. Ändra nu från 16777216 till 16777217 och jämför utskrifterna. Vad händer?
-
Byt typ på decimaltal till double och jämför resultatet. Vad händer?
-
Lägg till nya variabler
int big = 2147483647;
samtint bigger = big+1
. Gissa först vad som kommer att skrivas ut och skriv sedan ut värdena med System.out.println. Blev det som ni tänkt? Byt datatyp på bigger till en större, long, och jämför. Vad hände? -
Testa först att ändra biggers definition till
long bigger = big+1L;
och notera resultatet. Slutligen ändrar vi tilllong bigger = (long)big+1;
och jämför.
Info: Primitiva numeriska datatyper
Vi skall nu förklara vad vi sett.
Till att börja med sker konvertering implicit om den mottagande typen innehåller talområdet för den föregående typen, dvs från int och long sker implicit konvertering till float och double. På andra hållet krävs explicit konvertering eftersom det inte är säkert att den mottagande typen kan representera värdet.
Vad som inte skyddas mot är att man förlorar information vid konvertering mellan heltal och flyttalstyper. Vi såg exempel på det när 16777216 och 16777217 representerades av samma float. Detta beror på att flyttal kan representera ett större talområde, men innehåller bara samma mängd information som motsvarande heltal (int och float består av 32 bitar data medan long och double består av 64 bitar data). När vi bytte från float till double gick det däremot bra att behålla informationen om heltalet 16777217 eftersom det finns mer plats i en double vilken därför har noggrannare precision i de tal den representerar.
Vi får liknande problem när vi avrundar från float till int då
t.ex. (int)Math.PI
blir 3. Även har tappas information, men
detta är dock något vi är mer vana vid och därför mer sällan gör fel på.
I steg 6-7 undersökte vi relationen mellan int och long. Vi såg att om vi försöker representera ett tal större än vad som ryms i en int så slår talet runt och blir negativt. Detta är uppenbart något man behöver hålla koll på. Med hjälp av en long kan man minska risken att detta händer då mycket större tal kan representeras.
Vi såg att det inte sker någon automatisk konvertering från int till long
när talet blir för stort för en int. I vårt fall (long bigger =
big+1
) är det så att både big och 1 är av typen int vilket innebär
att resultatet av additionen blir en int som sedan tilldelas till en long,
men då är skadan redan skedd. För att få additionen att ske i storleken long
måste minst en av de båda ingående talen vara en long. Om det är olika
storlek/precision på de ingående talen kommer det mindre att konverteras
till den större typen och resultatet vara av den större typen. I vårt fall
uppnås detta genom att vi antingen deklarera konstanten 1 som en long (1L)
eller genom att explicit konvertera big till long.
Avslutning
Här slutar första laborationen. Visa slutresultatet för din handledare och passa på att fråga om det är något du undrar över. Det går också bra att redovisa ett senare tillfälle om handledaren är upptagen (dock senast enligt deadline).
Labb av Mikael Nilsson, Jonas Kvarnström 2014–2016.
Sidansvarig: Jonas Kvarnström
Senast uppdaterad: 2017-01-09