14.4. Præcis håndtering af undtagelser

Det kan have væsentlige konsekvenser, på hvilket niveau undtagelserne fanges, selv inden for samme metode.

Lad os bruge Tastatur til at lave et lille regneprogram, der lægger tal sammen. Vi spørger først brugeren, hvor mange tal det skal være (med læsTal()), og derefter kan han taste tallene ind. Til sidst spørger vi, om han vil prøve igen.

public class SumMedTastatur
{
  public static void main(String arg[])
  {
    Tastatur t = new Tastatur();
    boolean stop = false;
    try 
    {
      while (!stop)
      {
        System.out.print("Hvor mange tal vil du lægge sammen? ");
        double antalTal = t.læsTal();
        double sum = 0;

        for (int i=0; i<antalTal; i=i+1)
        {
          System.out.print("Indtast tal: ");
          sum = sum + t.læsTal();
        }
        System.out.println("Summen er: "+sum);
        System.out.print("Vil du prøve igen? (j/n)");
        if ("n".equals(t.læsLinje())) stop = true;    // undersøg om det er "n"
      }
    }   catch (Exception u) {
      System.out.println("Der opstod en undtagelse!");
      u.printStackTrace();
    }
  }
}

Resultatet bliver:

Hvor mange tal vil du lægge sammen? 2
Indtast tal: 1
Indtast tal: 2
Summen er: 3.0
Vil du prøve igen? j
Hvor mange tal vil du lægge sammen? 3
Indtast tal: 1
Indtast tal: 3
Indtast tal: 5
Summen er: 9.0
Vil du prøve igen? n

Brugeren taster og taster ... men hvad sker der, hvis han taster forkert?

Hvor mange tal vil du lægge sammen? 3
Indtast tal: 1
Indtast tal: 17xxøføf
Der opstod en undtagelse!
java.lang.NumberFormatException: 17xxøføf
        at java.lang.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1182)
        at java.lang.Double.parseDouble(Double.java:190)
        at Tastatur.læsTal(Tastatur.java:27)
        at SumMedTastatur.main(SumMedTastatur.java:18)

Her opstod en anden undtagelse: 17xxøføf kunne ikke konverteres til et tal. Igen er staksporet nyttigt til at finde fejlen (læst nedefra og op viser det, at main() i linje 18 kaldte læsTal(), der i linje 27 kaldte parseDouble(), der er en del af standardbiblioteket.

Programmet afslutter, da try-catch-blokken er yderst. En smartere opførsel ville være, at den igangværende sum blev afbrudt, og brugeren blev bedt om at starte forfra.

Det kan vi opnå ved at have try-catch inde i while-løkken:

public class SumMedTastatur2
{
  public static void main(String arg[])
  {
    Tastatur t = new Tastatur();
    boolean stop = false;

    while (!stop)
    {
      System.out.print("Hvor mange tal vil du lægge sammen? ");
      try 
      {
        double antalTal = t.læsTal();
        double sum = 0;

        for (int i=0; i<antalTal; i=i+1)
        {
          System.out.print("Indtast tal: ");
          sum = sum + t.læsTal();
        }
        System.out.println("Summen er: "+sum);
      }   catch (Exception u) {
        System.out.println("Indtastningsfejl - " + u);
      }
      System.out.print("Vil du prøve igen? (j/n)");
      if ("n".equals(t.læsLinje())) stop = true;
    }
  }
}

Resultatet bliver:

Hvor mange tal vil du lægge sammen? 5
Indtast tal: 1
Indtast tal: x2z
Indtastningsfejl - java.lang.NumberFormatException: x2z
Vil du prøve igen? j
Hvor mange tal vil du lægge sammen? 3
Indtast tal: 1200
Indtast tal: 1
Indtast tal: 1.9
Summen er: 1202.9
Vil du prøve igen? n

Hvis en undtagelse opstår, smides den aktuelle sum væk, og programmet spørger brugeren om han vil prøve igen med en ny sum (efter catch-blokken). Svarer han ja, starter programmet forfra i while-løkken.

Med omhyggelig placering af try-catch-blokke kan man altså kontrollere præcis hvordan programmet skal opføre sig i fejlsituationer:

Kode, hvori der kan opstå en undtagelse og efterfølgende afhængig kode, bør være i samme try-catch-blok

I eksemplet ovenfor finder vi først antallet af tal med læsTal(). Hvis det går galt, giver det heller ikke mening at gå i gang med at udregne en sum, da vi ikke ved, hvor mange tal den skal bestå af.