Ungefär motsvarande föreläsningsanteckningar från förra året: java012.pdf
Gäller även metodparametrar:final int gnorble = 17; gnorble = 4; // Ger kompileringsfel!
Som C++:s const, men inte lika avancerat. C++-exempel:void f(final Kanin k) { k = null; // Ger kompileringsfel! }
class Foo { public: const int k; int fum; void f(const int* const p) const { int i; k = 17; // Fel pga första const *p = 17; // Fel pga andra const p = &i; // Fel pga tredje const fum = 17; // Fel pga fjärde const } };
public class Heltal { private int talet; public void inkrementera() { ++talet; } private static int antal = 0; public final static int instansnummer = antal++; public static int konvertera(String s) { return Integer.parseInt(s); } public static void main(String[] args) { Heltal h = new Heltal(); Heltal.konvertera("13"); h.konvertera("13"); konvertera("13"); h.inkrementera(); } // main } // class Heltal
Exempel: Rektangelkalkylator.java, från föreläsning 1.
Körexempel:public class Complex { public static int antalKomplexaTal = 0; public Complex(float re, float im) { this.re = re; this.im = im; Complex.antalKomplexaTal++; } // Complex public Complex() { this(0, 0); } // Complex public void skrivTal() { System.out.print(re + " + " + im + "i" ); } // skrivTal } private float re, im; public static void main(String[] args) { Complex carray[] = new Complex[3]; for (int index = 0; index < 3; index++) { carray[index] = new Complex(); } // while System.out.println(Complex.antalKomplexaTal + " objekt allokerade."); } // main } // class Complex
pc105-246.oru.se complex > javac Complex.java pc105-246.oru.se complex > java Complex 3 objekt allokerade. pc105-246.oru.se complex >
Dessutom vet man inte alls när den kommer att köras, utom att det blir sen nån gång (kanske).
Mall för hur finalize egentligen ska se ut:protected void finalize() throws Throwable { antalKomplexaTal--; super.finalize(); } // finalize
protected void finalize() throws Throwable { try { // Egen uppstädning här } finally { super.finalize(); } } // finalize
Körexempel:public static void main(String[] args) { Complex carray[] = new Complex[3]; for (int index = 0; index < 3; index++) { carray[index] = new Complex(); } // while { Complex snartUrScope = new Complex(); System.out.println(antalKomplexaTal + " med snartUrScope."); } System.out.println(antalKomplexaTal + " utan snartUrScope."); } // main
pc105-246.oru.se complex-2 > javac Complex.java pc105-246.oru.se complex-2 > java Complex 4 med snartUrScope. 4 utan snartUrScope. pc105-246.oru.se complex-2 >
Jämför med pekare i C++ - måste tas bort manuellt med delete:public class Kanin { public static int antalSkapadeKaniner = 0; public static int antalKvarvarandeKaniner = 0; int nummer; // Funkar också: int nummer = antalSkapadeKaniner++; public Kanin() { nummer = ++antalSkapadeKaniner; ++antalKvarvarandeKaniner; System.out.println("Skapade just kanin nummer " + nummer + ". Nu finns " + antalKvarvarandeKaniner + " kaniner."); } // Kanin protected void finalize() throws Throwable { try { --antalKvarvarandeKaniner; System.out.println("Tar bort kanin nummer " + nummer + ". Sen finns " + antalKvarvarandeKaniner + " kaniner."); } finally { super.finalize(); } } // finalize public static void main(String[] args) { Kanin k; while (true) { k = new Kanin(); } } // main } // class Kanin
Jämför med objekt (ej pekare) i C++ - tas bort automatiskt, och direkt:Kanin *k; while (true) { k = new Kanin(); delete k; }
Javaprogrammet skapar massor av kaniner, men behåller bara pekaren till sen senaste. Alla de andra kan alltså skräpsamlas. Gör de det? Ja, sen nån gång.Kanin k; while (true) { k = Kanin(); }
Java-körexempel: Antal kaniner som funktion av antalet varv i while-loopen, för de första 100000 varven:
Först skapas alltså ca 4000 kaniner.
Sen tas ca 2000 kaniner bort.
Sen skapas ca 2000 till.
...
Plus att det blir konstigt, eftersom finalize kan köras till exempel mitt i en utskrift, eller när som helst garben känner för att gå igång:
Tar bort kanin nummer 2516. Sen finns 1503 kaniner. Tar bort kanin nummer 2628. Sen finns 1502 kaniner. Skapade just kanin nummer 3076. Nu finns 1613 kaniner. Tar bort kanin nummer 2515. Sen finns 1501 kaniner. Tar bort kanin nummer 2629. Sen finns 1500 kaniner.
Vad kan gå fel i följande C-program, unsafe-hello.c?
#include <stdio.h> int main(int argc, char* argv[]) { printf("Hello, world!\n"); return 0; } /* main */
En säkrare version, safe-hello.c:
#include <stdlib.h> #include <stdio.h> int main(int argc, char* argv[]) { if (printf("Hello, world!\n") != 14) { fprintf(stderr, "Kunde inte skriva hela texten!\n"); return EXIT_FAILURE; } if (fclose(stdout) != 0) { fprintf(stderr, "Kunde inte skriva till filen!\n"); return EXIT_FAILURE; } return 0; } /* main */
Liknelse: När man använder sig av ett subsystem (till exempel att man anropar metoden tvätta i objektet t av klassen Tvättmaskin) ska man inte behöva stå och kontrollera att den verkligen tvättar. I stället låter man den tvätta bäst den vill, och om något går fel kommer det ett undantagsobjekt flygande och träffar en i huvudet.
En bra sak med undantag är att de kan kastas "genom" en metod. Även om den metoden inte gör någon felhantering alls, kan det kastade undantaget skickas vidare till anroparen. Studera detta utdrag ur ett Java-program som inte innehåller någon felhantering (dinner-1/Dinner.java):
Vad händer om det uppstår ett fel någonstans, till exempel i drinkCoffee? Vi måste lägga till felhantering. Vi kan till exempel låta metoderna returnera true om allt gick bra, och false om något gick fel. Utdrag ur dinner-2/Dinner.java:private void drinkCoffee() { // Drick kaffe } private void eatDinner() { eatAppetizer(); eatMainCourse(); eatDessert(); drinkCoffee(); } public static void main(String[] args) { Dinner d = new Dinner(); d.eatDinner(); System.out.println("Ätandet klart."); System.out.println("(Kanske i alla fall.)"); } // main
Notera hur mycket jobb det blev i eatDinner, och hur det programmet egentligen ska göra nästan försvinner bland all felhanteringen.private boolean drinkCoffee() { if (coffeePot == null) return false; else { // Drick kaffe. Det gick bra: return true; } } private boolean eatDinner() { if (eatAppetizer() == false) return false; if (eatMainCourse() == false) return false; if (eatDessert() == false) return false; if (drinkCoffee() == false) return false; return true; } public static void main(String[] args) { Dinner d = new Dinner(); if (d.eatDinner() == false) System.out.println("Ätandet misslyckades!"); } // main
Bättre med undantag! Utdrag ur dinner-3/Dinner.java:
Så här ser NoCoffeePotException ut:private void drinkCoffee() throws NoCoffeePotException { if (coffeePot == null) throw new NoCoffeePotException(); else { // Drick kaffe } } private void eatDinner() throws NoCoffeePotException { eatAppetizer(); eatMainCourse(); eatDessert(); drinkCoffee(); } public static void main(String[] args) { Dinner d = new Dinner(); try { d.eatDinner(); } catch (NoCoffeePotException e) { System.out.println("Ingen kaffepanna!"); } catch (Exception e) { System.out.println("Något annat gick snett."); } } // main
class NoCoffeePotException extends Exception { } // class NoCoffeePotException
Checked exceptions ("kontrollerade undantag") måste fångas med try och catch, eller deklareras med throws. Unchecked exceptions ("okontrollerade undantag") behöver inte.
Körexempel:import java.io.BufferedReader; import java.io.InputStreamReader; // Att kastas vid division med noll class DivZeroException extends Exception { } // class DivZeroException class Number { public Number(int tal) { this.num = tal; } // Number public void addera(int tal) { this.num = this.num + tal; } // addera public void dividera(int tal) throws DivZeroException { if (tal == 0) throw new DivZeroException(); this.num = this.num / tal; } // dividera public int varde() { return this.num; } private int num = 0; public static void main(String[] args) { BufferedReader kbd_reader = new BufferedReader( new InputStreamReader(System.in)); int tal = 1; String buf; Number num = new Number(14); System.out.println("Startar med: " + num.varde()); System.out.println("Adderar udda tal."); System.out.println("Dividerar jämna tal." ); try { while (tal != 99) { System.out.print("Ge ett tal (99 avslutar): "); buf = kbd_reader.readLine(); tal = Integer.parseInt(buf); if (tal % 2 == 1) num.addera(tal); else try { num.dividera(tal); } catch (DivZeroException e) { System.out.println("Ojdå! Division med 0."); } System.out.println("Nu är talet: " + num.varde()); } // while } // try catch (Exception e) { System.out.println("Fel inmatning / tangentbordsfel"); } // catch } // main } // class Number
pc105-246.oru.se DivZeroException > javac Number.java pc105-246.oru.se DivZeroException > java Number Startar med: 14 Adderar udda tal. Dividerar jämna tal. Ge ett tal (99 avslutar): 13 Nu är talet: 27 Ge ett tal (99 avslutar): 4 Nu är talet: 6 Ge ett tal (99 avslutar): Bengt Fel inmatning / tangentbordsfel pc105-246.oru.se DivZeroException >
Klassen Sinus.java, som implementerar funktionen f(x) = sin(x):public interface Funktion { public float f(float x); public float derivatan(float x); public float anti_derivatan(float x); } // interface Funktion
Klassen Polynom.java, som implementerar funktionen f(x) = x - x2 + 3:public class Sinus implements Funktion { public float f(float x) { return (float)Math.sin(x); } public float derivatan(float x) { return (float)Math.cos(x); } public float anti_derivatan(float x) { return -(float)Math.cos(x); } } // class Sinus
Klassen Integral.java:public class Polynom implements Funktion { public float f(float x) { return x*x - x + 3.0f; } public float derivatan(float x) { return 2.0f*x - 1.0f; } public float anti_derivatan(float x) { return x*x*x/3.0f - x*x/2.0f + 3.0f*x; } } // Polynom
Klassen IntegralTest.java:public class Integral { public float integralen(Funktion fkn, float a, float b) { return fkn.anti_derivatan(b) - fkn.anti_derivatan(a); } } // Integral
Körexempel:import java.io.*; public class IntegralTest { public static void main(String[] args) { Sinus sinfunktion = new Sinus(); Polynom polynom = new Polynom(); Integral losare = new Integral(); System.out.println("Sinusintegral: " + losare.integralen(sinfunktion, 1.0f, 3.0f)); System.out.println("Polynomintegral: " + losare.integralen(polynom, 1.0f, 3.0f)); } // main } // IntegralTest
Sinusintegral: 1.5302948 Polynomintegral: 10.666667
Körexempel på Linux:package integralpaket; class Polynom implements Funktion { public float f(float x) { return x*x - x + 3.0f; } public float derivatan(float x) { return 2.0f*x - 1.0f; } public float anti_derivatan(float x) { return x*x*x/3.0f - x*x/2.0f + 3.0f*x; } } // Polynom
pc105-246.oru.se fo-02 > pwd /home/padrone/tmp/java/forelasningar/fo-02 pc105-246.oru.se fo-02 > echo $CLASSPATH /home/padrone/tmp/java/forelasningar/fo-02 pc105-246.oru.se fo-02 > java integralpaket.IntegralTest Sinusintegral: 1.5302948 Polynomintegral: 10.666667 pc105-246.oru.se fo-02 >