Les Exceptions

Liens pour comprendre mieux les exceptions: The Java Tutorial Lesson: Handling Errors with Exceptions (en Anglais)

Tout programme comporte des erreurs, même si celui-ci semble fonctionner à merveille. Chaque programmeur essaye de réduire au minimum le nombre d'erreurs, mais toutes les erreurs ne peuvent pas forcément être prévues.

Les erreurs syntaxiques sont la plupart interceptées lors de la compilation, mais il reste souvent des erreurs "imprévisibles". Ces erreurs se produisent généralement de façon exceptionnelle, c'est-à-dire suite à une action de l'utilisateur, ou de l'environnement. La solution consiste donc dans un premier temps à "prévoir les erreurs imprévisibles" en mettant en place un système de codes d'erreurs, c'est-à-dire un système permettant de retourner des valeurs spécifiques pour signaler un fonctionnement anormal de l'application (souvent -1 ou NULL).

Toutefois cette solution est loin d'être satisfaisante car:

  • elle rend difficile l'écriture de programmes
  • elle rend difficile la lecture du code source, à cause d'une embrication de test conditionnels (if .. else)
  • le code d'erreur retournée peut-être confondue avec la valeur retournée par la fonction... (-1 peut très bien être la valeur de retour d'une fonction)
Pour traiter les erreurs, Java propose un mécanisme qualifié d'exception, consistant à effectuer les instructions dans un bloc d'essai (le bloc try) qui surveille les instructions. Lors de l'apparition d'une erreur, celle-ci est lancée dans un bloc de traitement d'erreur (le bloc catch, appelé handler d'exception) sous forme d'un objet appelé Exception.

Il y a deux types d'exceptions, runtime exceptions et checked exceptions. On a besoin d'attraper les exceptions "checked" mais ce n'est pas obligitoire d'attraper les exceptions "runtime". De toute façon, il y a un trop grand nombre d'exceptions "runtime" pour attraper tous.

Example: Attraper un exception checked

import java.io.*;

public class OuvrirFichierMal {

    FileReader f;
   
    OuvrirFichierMal(String fNom) {

-->>>>	f=new FileReader(new File(fNom));

	System.out.println(fNom + " est ouvert");

    }
    

    public static void  main (String [] args) {
	OuvrirFichierMal f;
	f = new OuvrirFichierMal("index.html");
	f = new OuvrirFichierMal("FichierNExistPas.txt");
    }
}


-------

import java.io.*;

public class FileException {

    FileReader f;
   
    FileException(fNom fn) {
	try {
	    f=new FileReader(new File(fn));
	} catch (FileNotFoundException e) {
	    System.out.println(e);
	}
	System.out.println("Opened fn");
	f.close();
    }
    

    public static void  main (String [] args) {
	FileException f = new FileException();
    }
}

Pour tirer profit des exceptions, vous devez faire quelque chose de futé dans la section "catch". Voici une meilleure version:
import java.io.*;
import javax.swing.*;
public class OuvrirFichier2 {

    FileReader f;
   
    OuvrirFichier2(String fNom) {

	f=null;
	while (f==null) {
	    try {
		f=new FileReader(new File(fNom));
		System.out.println(fNom + " est ouvert");	    
		return;
	    } catch (FileNotFoundException e) {
		System.out.println("Fichier "+ fNom + " n'existe pas");

		fNom=getNom(); //demander à l'utilisateur un nom de fichier

	    }
	}
    }


    //les détails ne sont pas importants
    private String getNom() {
	String txt=null;
	try {
	    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	    System.out.print("Fichier d'ouvrir? > ");
	    txt=in.readLine();
	} catch (Exception e) {
	    System.out.println(e);
	    System.exit(0);
	}
	return txt;
    }	

    public static void  main (String [] args) {
	OuvrirFichier2 f;
	f = new OuvrirFichier2("index.html");
	f = new OuvrirFichier2("googoo.txt");
    }
}

Advantage 1: Séparation du code de gestion
d'erreur du code "régular"

Pseudocode! (Ce n'est pas Java):

lireFichier() {
 //ouvrir le fichier 
 //déterminer la taille
 //assigner assez de mémoire
 //lire le fichier dans la mémoire
 //fermer le fichier
}


erreurCodeType lireFile {
    initializer erreurCode = 0;
    //ouvrir le fichier
    if (fichierOuvert) {
	//déterminer la taille du fichier
        if (tailleTrouvé) {
	    //assigner assez de mémoire
            if (assezDeMémoire) {
                //lire le fichier dans la mémoire
                if (fichierPasLu) { 
                    erreurCode = -1;
                }
            } else {
                erreurCode = -2;
            }
        } else {
            erreurCode = -3;
        }
        //fermer le fichier
        if (fichierPasFermé && erreurCode == 0) {
            erreurCode = -4;
        } else {
            erreurCode = erreurCode and -4;
        }
    } else {
        erreurCode = -5;
    }
    return erreurCode;
}
 

Pseudocode! (Ce n'est pas Java)

readFile {
    try {
      //ouvrir le fichier 
      //déterminer la taille
      //assigner assez de mémoire
      //lire le fichier dans la mémoire
      //fermer le fichier
    } catch (fichierPasOuvert) {
        faireQuelqueChose;
    } catch (taillePasTrouvé) {
        faireQuelqueChose;
    } catch (pasAssezDeMémoire) {
        faireQuelqueChose;
    } catch (fichierPasLu) {
        faireQuelqueChose;
    } catch (fichierPasFermé) {
        faireQuelqueChose;
    }
}

Advantage 2: La propagation des erreurs
vers la pile d'exécution

class TesterException1 {
    String [] s;
    TesterException1() {
	f1();
    }
    void f1() {
	f2();
    }
    void f2() {
	f3();
    }
    void f3() {
	f4();
    }
    void f4() {
	s[100]="crasher!"; 
    }
    public static void  main (String [] args) {
	TesterException1 t = new TesterException1();
    }
}

534 eckdoug@bach ~/ift1020/02_04>java TesterException1 
Exception in thread "main" java.lang.NullPointerException
        at TesterException1.f4(TesterException1.java:22)
        at TesterException1.f3(TesterException1.java:18)
        at TesterException1.f2(TesterException1.java:14)
        at TesterException1.f1(TesterException1.java:10)
        at TesterException1.(TesterException1.java:6)
        at TesterException1.main(TesterException1.java:26)
Une pile ("stack" en anglais) est une liste où les insertions et les suppresions se font toutes du même côté LIFO. Java utilise une pile pour controller l'exécution du programme. Chaque fois q'on appelle une fonction, elle est mise sur la pile. Quand on quitte la fonction, ell est enlevée de la pile.

Quand il y a une erreur, la pile est laissée intacte. L'exception monte la pile.
C'est possible d'attraper l'exception l'erreur à n'importe quel niveau.

Advantage 3: Capacité de grouper des
erreurs des types semblables


Par example, ArrayException hérite de Exception.
InvalidIndexException, ElementTypeException and NoSuchElementException héritent de ArrayException.

On peut les attraper ensemble ou individuellement:

try {
    //quelque chose avec un Array
} catch (ArrayException e) {

}


try {
    //quelque chose avec un Array
} catch (InvalidIndexException e) {

} catch (ElementTypeException e) {

} catch (NoSuchElementExcepion e) {

}

"Finally"

Java fournit un méchanisme pour nettoyer.
    OuvrirFichier(String fNom) {
	try {
	    f=new FileReader(new File(fNom));
            System.out.println(fNom + " est ouvert");
	} catch (FileNotFoundException e) {
	    System.out.println(e);
	    System.exit(0);
	} finally {
            if ( f != null) {
          f.close();
        }
    }
}

"Throw"

Vous pouvez produire des exceptions vous-même avec le mot-clé throws. C'est relativement rare de faire ça.

Voici un example. Pour le code complet, voir Les Packages.

package orange;
public class EmptyOrangeSacException extends Exception {
    EmptyOrangeSacException() {
	super("Le sac est vide.");
    }
}

----

package orange;
import java.util.Vector;

//doit etre public
public class OrangeSac extends Vector {
    private int jours=0;    //pas protected
    private String origine; //pas protected    
    
    
    ...

    public Orange removeOrange() throws EmptyOrangeSacException {
	if (size()==0) 
	    throw new EmptyOrangeSacException();
	Orange o = (Orange)(firstElement());
	removeElementAt(0);
	return o;
    }


}

Les Examples

OuvrirFichierMal.java

OuvrirFichier.java

OuvrirFichier2.java

VoirPileExecution.java

Retour à la page principale d'ift1020
Last modified: Sun Oct 3 10:56:50 EDT 2004 par Douglas Eck [douglas D0T eck AT umontreal D0T ca]