Feilmeldinger i Java

Vi kan kaste feilmeldinger:

throw new SomeException();

Vi kan fange opp feilmeldinger og gjøre noe med dem:

try {
    // something
} catch (SomeException e) {
    // when SomeException happens do this
}

Og vi kan ignorere feilmeldinger og kaste dem videre:

void someMethod() throws SomeException {}

Så hva er en Exception?

En Exception er:

  • En vanlig klasse
  • Som arver fra Exception (direkte eller indirekte)
  • Og representerer en eller annen feil som har oppstått

Å definere sin egen Exception er så lett som:

class MyException extends Exception {}

Så kan man kaste denne slik:

throw new MyException();

Men veldig ofte finnes det allerede en passende Exception:

  • NoSuchElementException
  • IllegalStateException
  • NullPointerException
  • OperationNotSupportedException
  • IndexOutOfBoundsException
  • FileNotFoundException

Du kan også arve fra disse i stedet for fra Exception

Men for å skjønne Exceptions skikkelig er det viktig å skjønne funksjonskall. For hva skjer egentlig når vi kjører dette? Tegnetid!

public static void main(String[] args) {
    a();
    System.out.println("Bar!");
}

public static void a() {
    b();
}

public static void b() {
    c();
}

public static void c() {
    System.out.println("Foo!");
}

La oss så legge til Exceptions:

class HelloException extends Exception {}

public static void main(String[] args) throws HelloException {
    a();
    System.out.println("Bar!");
}

public static void a() throws HelloException {
    b();
}

public static void b() throws HelloException {
    c();
}

public static void c() throws HelloException {
    System.out.println("Foo!");
    throw new HelloException();
}

RuntimeException

RuntimeException er en variant av Exception som vanligvis representerer programmeringsfeil. Eksempler er:

  • NullPointerException
  • ArrayIndexOutOfBoundsException

Når man kaster en RuntimeException trenger man ikke være like nøye med å si hvordan de skal håndteres, for de skal jo vanligvis ikke oppstå(i motsetning til vanlige Exceptions).

Det forrige eksempelet med RuntimeException:

class ShouldNotHappenException extends RuntimeException {}

public static void main(String[] args) {
    a();
    System.out.println("Bar!");
}

public static void a() {
    b();
}

public static void b() {
    c();
}

public static void c() {
    System.out.println("Foo!");
    throw new ShouldNotHappenException();
}

try/catch

Å håndtere feil

For å håndtere feil har vi i Java try-catch-blokken:

try {
    // something
} catch (SomeException e) {
    // when SomeException happens do this
}

Ikke gjør dette:

try {
    // something
} catch (Exception e) {}
  • Ikke prøv å fang alle feilmeldinger på én gang
  • Ikke fang feilmeldinger uten å gjøre noe i det hele tatt

Hvis du ikke kan/gidder gjøre noe for å fikse en feil, i alle fall gjør det lett for deg selv å se hva som gikk galt.

try {
    // something with IO
} catch (IOException e) {
    // Skriv ut informasjon om hvor feilen skjedde
    e.printStackTrace();
    // Avslutt programmet
    System.exit(1);
}

finally

Etter en try-catch-blokk kan man ha en finally-blokk, som kjøres uansett hva som skjer i try-catch-blokken:

try {
    // something
} catch (SomeException e) {
    // when SomeException happens do this
} finally {
    // whatever happens do this
}

Den vanligste bruken av finally er å lukke filer:

Scanner sc = null;
try {
    sc = new Scanner(new File("input.txt"));
    // do something with sc
} catch (IOException e) {
    // when we get an IOException do this
} finally {
    if (sc != null) sc.close();
}

Men dette er jo noe herk...

Heldigvis fikk vi i Java 7 denne syntaksen som kalles try-with-resources:

try (Scanner sc = new Scanner(new File("input.txt"))) {
    // do something with sc
    // sc.close() will be called automatically
} catch (IOException e) {
    // when we get an IOException do this
}

For at noe skal kunne brukes med try-with-resources må det bare implementere grensesnittet AutoCloseable

La oss så se på eksempelet vårt med try-catch:

class HelloException extends Exception {}

public static void main(String[] args) {
    try {
        a();
    } catch (HelloException e) {
        System.out.println("Woops! Exception happened!");
    }
    System.out.println("Bar!");
}

public static void a() throws HelloException {
    b();
}

public static void b() throws HelloException {
    c();
}

public static void c() throws HelloException {
    System.out.println("Foo!");
    throw new HelloException();
}

Og et enkelt eksempel med PrintWriter:

public static void main(String[] args) {
    try (PrintWriter out = new PrintWriter("out.txt")) {
        out.println("Hello world!");
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    // Hvis vi har mer kode her vil den bli kjørt,
    // uavhengig av feil i koden over
}

Så noen detaljer dere kanskje ikke kommer til å trenge bruke med det første, men som kan være kjekt å vite om...

En try-blokk kan ha flere catch-blokker:

try (Scanner sc = new Scanner(new File("number.txt"))) {
    someNumber += sc.nextInt();
} catch (FileNotFoundException e) {
    System.out.println("Could not find number.txt!");
} catch (InputMismatchException e) {
    System.out.println("First element was not a number!");
} catch (NoSuchElementException e) {
    System.out.println("number.txt was too short!");
}

En catch-blokk kan ta flere feilmeldinger i samme blokk:

try (Scanner sc = new Scanner(new File("number.txt"))) {
    someNumber += sc.nextInt();
} catch (FileNotFoundException e) {
    System.out.println("Could not find number.txt!");
} catch (InputMismatchException | NoSuchElementException e) {
    System.out.println("Bad content in number.txt!");
}

Feilmeldinger kan "pakkes inn" i andre feilmeldinger. Dette kalles exception chaining.

try (Scanner sc = new Scanner(new File("number.txt"))) {
    someNumber += sc.nextInt();
} catch (FileNotFoundException
       | InputMismatchException
       | NoSuchElementException e) {
    throw new IOException(e);
}

Videre lesning:

Made with Slides.com