programmera.net -> java -> normal     för utskrift      info@programmera.net

Undantag

1. Undantag
2. RuntimeException
3. getMessage()
4. Finally
5. throws
6. throw

1. Undantag

I java gör man skillnad på fel och undantag:

  • Fel (klassen Error): Något allvarligt fel som inte har med användaren att göra, felet ska inte fångas utan avslutar applikationen omedlebart. Ett exempel på denna typ av fel är när OutOfMemoryError.
  • Undantag: Ett mindre allvarligt fel som inte nödvändigtvis krashar hela applikationen. Alla undantag är subklasser av throwable, vilket betyder att ett undantag kan fångas och hanteras av programmet. Det finns två typer av undantag:
    • Klassen Exception: Denna typ av undantag kan bara inträffa inuti ett try-block. Försöker man använda en metod som kastar denna typ av undantag utanför ett try-block kommer kompilatorn att protestera. Exempel på denna typ av fel är FileNotFoundException och IOException.
    • Klassen RuntimeException: Denna typ av undantag kan inträffa utanför ett try-block. De behöver alltså inte fångas. Om de inte fångas avbryter de exekveringen av programmet. Ett typiskt fel är NullPointerException eller ArrayIndexOutOfBoundsException.

2. RuntimeException

Vi testar att göra ett program som genererar ett runtime-undantag:
class TestDivZeroError{
  public static void main(String[] args){
    int i=1;
    i=i/0;
  }
}
Vi kör programmet:
[olle@dev1]$ java TestDivZeroError
Exception in thread "main" java.lang.ArithmeticException: / by zero
      at TestDivZeroError.main(TestDivZeroError.java:4)
Programmet genererade ett fel, och felet skrevs ut på skärmen. Undantaget ArithmeticException ärver av typen RuntimeException och behöver alltså inte fångas. Detta underlättar för programmeraren som alltså inte behöver göra ett try-block vid varje division. Vi kan enkelt fånga felet genom att skriva om programmet till:
class TestDivZero{
  public static void main(String[] args){
    int i=1;
    try{
      i=i/0;
      System.out.println("Division ok! i="+i);
    }catch(ArithmeticException ae){
      System.out.println("ArithmeticException cuz weve been bad!");
    }catch(Exception e){
      System.out.println("Something else is wrong");
    }
  }
}
Vi kör programmet:
[olle@dev1]$ java TestDivZero
ArithmeticException cuz weve been bad!
Här ser vi att den första catch-satsen fångar undantaget, och därför nås aldrig den andra catch-satsen. Det är vanligt att man först försöker fånga specifika undantag och sedan de allmänna.

3. getMessage()

Fångar man ett allmänt fel kan man få en ledtråd om vad som gick fel genom att använda getMessage()-metoden som innehåller ett meddelande om felet. Alternativt använder man printStackTrace()-metoden. Vi gör ett exempel:
class TestDivZeroMessage{
  public static void main(String[] args){
    int i=1;
    try{
      i=i/0;
      System.out.println("Division ok! i="+i);
    }catch(Exception e){
      System.out.println("Meddelande: "+e.getMessage());
      System.out.println("Stacken: ");
      e.printStackTrace();
    }
  }
}
Vi får utskriften:
[olle@dev1]$ java TestDivZeroMessage
Meddelande: / by zero
Stacken: 
java.lang.ArithmeticException: / by zero
        at TestDivZeroMessage.main(TestDivZeroMessage.java:5)
Vi känner igen utskriften från printStackTrace(), det är samma utskrift man får då man inte har fångat ett fel. Utskriften från printStackTrace() kan bli väldigt lång i större program.

4. Finally

Nyckelordet finally läggs sist i ett try-block, och körs alltid, oavsett om ett undantag kastas eller inte. Vi skriver om exemplet ovan med finally:
class TestDivZeroFinally{
  public static void main(String[] args){
    int i=1;
    try{
      i=i/0;
      System.out.println("Division ok! i="+i);
    }catch(ArithmeticException ae){
      System.out.println("ArithmeticException cuz weve been bad!");
    }catch(Exception e){
      System.out.println("Something else is wrong");
    }finally{
      System.out.println("Finally");
    }
  }
}
Vi kör programmet:
[olle@dev1]$ java TestDivZeroFinally
ArithmeticException cuz weve been bad!
Finally 
Här ser vi att "Finally" skrivs trots att en catch-sats har fångat felet. Nu ändrar vi i programmet så att det inte kastar något fel, och tittar om finally anropas:
class TestAddZeroFinally{
  public static void main(String[] args){
    int i=1;
    try{
      i=i+0;
      System.out.println("Add ok! i="+i);
    }catch(ArithmeticException ae){
      System.out.println("ArithmeticException cuz weve been bad!");
    }catch(Exception e){
      System.out.println("Something else is wrong");
    }finally{
      System.out.println("Finally");
    }
  }
}
Vi kör programmet:
[olle@dev1]$ java TestAddZeroFinally
Add ok! i=1
Finally

5. throws

Om en metod inte vill hantera ett undantag på egen hand kan metoden deklareras med throws vilket betyder att den anropande metoden tar hand om eventuella fel. Titta på exemplet:
class TestDivFunc{
  public static void main(String[] args){
    int i=1;
    try{
      i=divZero(i);
      System.out.println("Division ok! i="+i);
    }catch(ArithmeticException ae){
      System.out.println("ArithmeticException cuz weve been bad!");
    }catch(Exception e){
      System.out.println("Something else is wrong");
    }	  
  }
  static int divZero(int x) throws ArithmeticException{
      x=x/0;
      return x;
  }
}
När vi kör det får vi resultatet:
[olle@dev1]$ java TestDivFunc
ArithmeticException cuz weve been bad!
I det här fallet hade vi inte behövt throws eftersom ArithmeticException ärver från RuntimeException och inte behöver fångas. Exemplet ovan hade genererat samma utskrift även utan throws. Om undantaget ej ärver från RuntimeException är throws obligatoriskt, alternativt att undantaget tas om hand på plats.

6. throw

Med detta nyckelord kastar man ett undantag. Detta kan vara bra i två fall:

  1. Du vill generera ett undantag som markerar att något gått snett.
  2. Du fångar ett undantag och använder det, men vill ändå kasta det vidare.
Vi testar metod 2:
class TestDivZeroThrow{
  public static void main(String[] args){
    int i=1;
    try{
      i=i/0;
      System.out.println("Division ok! i="+i);
    }catch(ArithmeticException ae){
      System.out.println("ArithmeticException cuz weve been bad!");
      throw ae;
    }catch(Exception e){
      System.out.println("Something else is wrong");
    }
  }
}
Programmet skriver:
[olle@dev1]$ java TestDivZeroThrow
ArithmeticException cuz weve been bad!
Exception in thread "main" java.lang.ArithmeticException: / by zero
     at TestDivZeroThrow.main(TestDivZeroThrow.java:5)
Vi ser att felet både hanteras och kastas vidare.