Version without JavaScript, read-only: workshops, quizzes and running code are not available here. Full version · Catalogue · Sign in

Lecture notes · chapter 9

Exceptions

What happens to execution when an operation fails, the difference between checked and unchecked exceptions, and the common ways of hiding an error unintentionally.

This chapter extends sequences 7, 8 and 9.

Return codes and exceptions

int prix = canard.getPrix();
if (prix == -1) { /* error? or a free duck? */ }

An error code can be confused with a valid value, it can be ignored without anything reporting it, and it has to be passed on by hand from one caller to the next. An exception interrupts the normal course of the program, carries a message, and cannot be ignored through simple oversight.

public void setPrix(int prix) {
    if (prix < 0) throw new IllegalArgumentException("prix >= 0");
    this.prix = prix;
}

throw interrupts the method immediately: the following statements are not executed.

try, catch, finally

try {
    CanardEnPlastique c = new CanardEnPlastique("Jean", -50);
    System.out.println("jamais atteint");
} catch (IllegalArgumentException e) {
    System.out.println("Erreur : " + e.getMessage());
} finally {
    System.out.println("toujours exécuté : on vide le bassin");
}
  • try contains the normal processing;
  • catch says what to do on failure, depending on the type of exception;
  • finally contains the clean-up, executed in every case: success, failure, and even after a return.

return in a finally

Before running the program, try to predict what lireProfondeur() returns and what becomes of the exception.

Finally.java

class Bassin {

    /** A measurement that fails. */
    static int mesurer() {
        throw new IllegalStateException("sonde débranchée");
    }

    /** Faulty version: a return in the finally. */
    static int lireProfondeur() {
        try {
            return mesurer();
        } finally {
            return -1;                  // ← swallows everything
        }
    }

    /** Faulty version no. 2: the catch that says nothing. */
    static int lireTemperature() {
        try {
            return mesurer();
        } catch (Exception e) {
            return 0;                   // the error has left no trace
        }
    }

    /** Correct version: we clean up, we do not decide in the caller's place. */
    static int lireCorrectement() {
        try {
            return mesurer();
        } finally {
            System.out.println("   (nettoyage : sonde remise en place)");
        }
    }

    public static void main(String[] args) {

        System.out.println("1. lireProfondeur()  = " + lireProfondeur());
        System.out.println("2. lireTemperature() = " + lireTemperature());

        System.out.println("3. lireCorrectement() :");
        try {
            System.out.println("   valeur = " + lireCorrectement());
        } catch (IllegalStateException e) {
            System.out.println("   l'exception est bien remontée : " + e.getMessage());
        }
    }
}

A workshop goes here: you write and run code in it. It requires JavaScript.

Checked or unchecked

classDiagram
    direction TB
    class Throwable
    class Error["Error (JVM problems)"]
    class Exception
    class RuntimeException["RuntimeException (unchecked)"]
    class IOException["IOException (checked)"]
    class NullPointerException
    class IllegalArgumentException

    Throwable <|-- Error
    Throwable <|-- Exception
    Exception <|-- RuntimeException
    Exception <|-- IOException
    RuntimeException <|-- NullPointerException
    RuntimeException <|-- IllegalArgumentException

For a checked exception, the compiler requires it to be declared (throws) or handled (catch). It signals an external event that a correct program must anticipate, such as a missing file or a network outage.

For an unchecked exception, the compiler requires nothing. It signals that an invariant does not hold, that is, a bug: an invalid argument, a null reference.

Defining your own exception

public class PrixCanardException extends Exception {
    public PrixCanardException(String message) { super(message); }
}

public void setPrix(int prix) throws PrixCanardException {
    if (prix < 0) throw new PrixCanardException("Prix négatif interdit !");
    this.prix = prix;
}

With extends Exception, the exception is checked and the caller will have to handle it; with extends RuntimeException, it is unchecked. The parent class determines the category: there is no keyword for it.

A named business exception is preferable to a generic IllegalArgumentException, because it can be caught separately and its name indicates what happened.

Hiding an error unintentionally

Good practice

  • Catch the precise exception type, not Exception for convenience.
  • Write a useful message, stating what was expected and what was received. A message such as "Erreur" does not help find the problem.
  • Keep the cause by passing it to the constructor: throw new TraitementException("import du bassin", e), with the e.
  • Do not use exceptions to control the course of the program: leaving a loop with a throw, for example, is a misuse.
Exam question

What is the difference between a checked and an unchecked exception?

A checked exception must be declared or handled, otherwise the compiler rejects the code; it signals a foreseeable external event. An unchecked exception (RuntimeException and its subclasses) imposes nothing and signals a programming error. The category depends on the parent class.