Précédent Remonter Suivant

Annexe A  Compléments

A.1  Exceptions

Les exceptions sont des objets de la classe Exception. Il existe aussi une classe Error moins utilisée pour les erreurs système. Toutes les deux sont des sous-classes de la classe Throwable, dont tous les objets peuvent être appliqués à l'opérateur throw, comme suit:

throw e ;
Ainsi on peut écrire en se servant de deux constructeurs de la classe Exception:

throw new Exception();
throw new Exception ("Accès interdit dans un tableau");
Heureusement, dans les classes des bibliothèques standard, beaucoup d'exceptions sont déjà pré-définies, par exemple IndexOutOfBoundsException. On récupère une exception par l'instruction try ...catch. Par exemple

try {
  // un programme compliqué
} catch ( IOException e) {
  // essayer de réparer cette erreur d'entrée/sortie
}
catch ( Exception e) {
  //  essayer de réparer cette erreur plus générale
}
Si on veut faire un traitement uniforme en fin de l'instruction try, que l'on passe ou non par une exception, que le contrôle sorte ou non de l'instruction par une rupture de séquence comme un return, break, etc, on écrit

try {
  // un programme compliqué 
} catch ( IOException e) {
  //  essayer de réparer cette erreur d'entrée/sortie
}
catch ( Exception e) {
  //essayer de réparer cette erreur plus générale
}
finally {
  // un peu de nettoyage
}
Il y a deux types d'exceptions. On doit déclarer les exceptions vérifiées derrière le mot-clé throws dans la signature des fonctions qui les lèvent. Ce n'est pas la peine pour les exceptions non vérifiées qui se reconnaissent en appartenant à une sous-classe de la classe RuntimeException. Ainsi

static int lire () throws IOException, ParseException {
  int n;
  BufferedReader in =
      new BufferedReader(new InputStreamReader(System.in));

  System.out.print ("Taille du carré magique, svp?::  ");
  n = Integer.parseInt (in.readLine());
  if ((n <= 0) || (n > N) || (n % 2 == 0))
      erreur ("Taille impossible.");
  return n;
}

A.2  La classe MacLib

Cette classe est l'oeuvre de Philippe Chassignet, et elle a survécu à l'évolution de l'enseignement d'informatique à l'X. Jusqu'en 1992, elle permettait de faire afficher sur un écran de Macintosh des dessins produits sur un Vax (via TGiX, autre interface du même auteur). Elle a ensuite été adaptée en 1994 à ThinkPascal sur Macintosh, puis TurboPascal sous PC-Windows; puis à ThinkC et TurboC, X11 ; DelphiPascal, Borland C (nouveau Windows) ; CodeWarrior Pascal et C, tout ça dans la période 1996--1998. En 1998 elle a commencé une nouvelle adaptation, avec Java, qui a constitué une simplification énorme du travail, la même version marchant sur toutes les plateformes ! Elle est désormais interfacée avec l'AWT (Abstract Windowing Toolkit), avec un usage souple de la boucle d'événement.

A.2.1  Fonctions élémentaires

Les fonctions sont inspirées de la libraire QuickDraw du Macintosh. La méthode initQuickDraw() -- dont l'utilisation est impérative et doit précéder toute opération de dessin -- permet d'initialiser la fenêtre de dessin. Cette fenêtre Drawing créée par défaut permet de gérer un écran de 1024 × 768 points. L'origine du système de coordonnées est en haut et à gauche. L'axe des x va classiquement de la gauche vers la droite, l'axe des y va plus curieusement du haut vers le bas (c'est une vieille tradition de l'informatique, dure à remettre en cause). En QuickDraw, x et y sont souvent appelés h (horizontal) et v (vertical). Il y a une notion de point courant et de crayon avec une taille et une couleur courantes. On peut déplacer le crayon, en le levant ou en dessinant des vecteurs par les fonctions suivantes
moveTo (x, y)
Déplace le crayon aux coordonnées absolues x, y.

move (dx, dy)
Déplace le crayon en relatif de dx, dy.

lineTo (x, y)
Trace une ligne depuis le point courant jusqu'au point de coordonnées x, y.

line (dx, dy)
Trace le vecteur (dx, dy) depuis le point courant.

penSize(dx, dy)
Change la taille du crayon. La taille par défaut est (1, 1). Toutes les opérations de tracé peuvent se faire avec une certaine épaisseur du crayon.

penMode(mode)
Change le mode d'écriture: patCopy (mode par défaut qui remplace ce sur quoi on trace), patXor (mode Xor, i.e. en inversant ce sur quoi on trace).

A.2.2  Rectangles

Certaines opérations sont possibles sur les rectangles. Un rectangle r a un type prédéfini Rect. Ce type est une classe qui a le format suivant

public class Rect {

  short left, top, right, bottom;
}
Fort heureusement, il n'y a pas besoin de connaître le format internes des rectangles, et on peut faire simplement les opérations graphiques suivantes sur les rectangles

setRect(r, g, h, d, b)
fixe les coordonnées (gauche, haut, droite, bas) du rectangle r. C'est équivalent à faire les opérations r.left := g;, r.top := h;, r.right := d;, r.bottom := b. Le rectangle r doit déjà avoir été construit.

unionRect(r1, r2, r)
définit le rectangle r comme l'enveloppe englobante des rectangles r1 et r2. Le rectangle r doit déjà avoir été construit.

frameRect(r)
dessine le cadre du rectangle r avec la largeur, la couleur et le mode du crayon courant.

paintRect(r)
remplit l'intérieur du rectangle r avec la couleur courante.

invertRect(r)
inverse la couleur du rectangle r.

eraseRect(r)
efface le rectangle r.

drawChar(c), drawString(s)
affiche le caractère c ou la chaîne s au point courant dans la fenêtre graphique. Ces fonctions diffèrent de write ou writeln qui écrivent dans la fenêtre texte.

frameOval(r)
dessine le cadre de l'ellipse inscrite dans le rectangle r avec la largeur, la couleur et le mode du crayon courant.

paintOval(r)
remplit l'ellipse inscrite dans le rectangle r avec la couleur courante.

invertOval(r)
inverse l'ellipse inscrite dans r.

eraseOval(r)
efface l'ellipse inscrite dans r.

frameArc(r,start,arc)
dessine l'arc de l'ellipse inscrite dans le rectangle r démarrant à l'angle start et sur la longueur définie par l'angle arc.

frameArc(r,start,arc)
peint le camembert correspondant à l'arc précédent .... Il y a aussi des fonctions pour les rectangles avec des coins arrondis.

button()
est une fonction qui renvoie la valeur vraie si le bouton de la souris est enfoncé, faux sinon.

getMouse(p)
renvoie dans p le point de coordonnées (p.h, p.v) courantes du curseur.

A.2.3  La classe Maclib

class Point {
  short h, v;

  Point(int h, int v) {
    h = (short)h;
    v = (short)v;
  }
}
class MacLib {

  static void setPt(Point p, int h, int v) {..}
  static void addPt(Point src, Point dst) {...}
  static void subPt(Point src, Point dst) {...}
  static boolean equalPt(Point p1, Point p2) {...}
  ...
}
Et les fonctions correspondantes (voir page ??)

static void setRect(Rect r, int left, int top, int right, int bottom)
static void unionRect(Rect src1, Rect src2, Rect dst)

static void frameRect(Rect r)
static void paintRect(Rect r)
static void eraseRect(Rect r)
static void invertRect(Rect r)

static void frameOval(Rect r)
static void paintOval(Rect r)
static void eraseOval(Rect r)
static void invertOval(Rect r)

static void frameArc(Rect r, int startAngle, int arcAngle)
static void paintArc(Rect r, int startAngle, int arcAngle)
static void eraseArc(Rect r, int startAngle, int arcAngle)
static void invertArc(Rect r, int startAngle, int arcAngle)
static boolean button()
static void getMouse(Point p)
static void getClick(Point p)
Toutes ces définitions sont aussi sur les stations de travail, dans le fichier
/usr/local/lib/MacLib-java/MacLib.java
On veillera à avoir cette classe dans l'ensemble des classes chargeables (variable d'environnement CLASSPATH).

A.2.4  Jeu de balle

Le programme suivant fait rebondir une balle dans un rectangle, première étape vers un jeu de pong.
class Pong{

  static final int  C  =  5, // Le rayon de la balle 
      X0 =  5, X1 =  250,
      Y0 =  5, Y1 =  180;

  public static void main(String args[]) {
      int   x, y, dx, dy;
      Rect  r = new Rect();
      Rect  s = new Rect();
      Point p = new Point();
      int  i;

      // Initialisation du graphique
      MacLib.initQuickDraw();
      MacLib.setRect(s, 50, 50, X1 + 100, Y1 + 100);
      MacLib.setDrawingRect(s);
      MacLib.showDrawing();
      MacLib.setRect(s, X0, Y0, X1, Y1);

      // le rectangle de jeu
      MacLib.frameRect(s);
      // on attend un click et on note les coordonnées 
      // du pointeur

      MacLib.getClick(p);
      x = p.h; y = p.v;
      // la vitesse initiale de la balle
      dx = 1;
      dy = 1;
      while(true){
           MacLib.setRect(r, x - C, y - C, x + C, y + C);
           // on dessine la balle en x,y
           MacLib.paintOval(r);
           x = x + dx;
           if(x - C <= X0 + 1 || x + C >= X1 - 1)
               dx = -dx;
           y = y + dy;
           if(y - C <= Y0 + 1 || y + C >= Y1 - 1)
               dy = -dy;
           // On temporise
           for(i = 1; i <= 2500; ++i)
               ;
           // On efface la balle
           MacLib.invertOval(r);
      }
  }
}

A.3  La classe TC

Le but de cette classe écrite spécialement pour le cours par l'auteur du présent poly (F. Morain) est de fournir quelques fonctions pratiques pour les TP, comme des entrées-sorties faciles, ou encore un chronomètre. Les exemples qui suivent sont presque tous donnés dans la classe TestTC qui est dans le même fichier que TC.java (qui se trouve lui-même accessible à partir des pages web du cours).

A.3.1  Fonctionnalités, exemples

Gestion du terminal

Le deuxième exemple de programmation consiste à demander un entier à l'utilisateur et affiche son carré. Avec la classe TC, on écrit :
class TCex{
    public static void main(String[] args){
        int n;

        System.out.print("Entrer n=");
        n = TC.lireInt();
        System.out.println("n^2=" + (n*n));
    }
}

La classe TC contient d'autres primitives de ce type, comme TC.lireLong, ou encore lireLigne.

Si l'on veut lire plusieurs nombres1, ou bien si on veut les lire sur l'entrée standard, par exemple en exécutant
unix% java prgm < fichier
il convient d'utiliser la syntaxe :
  do{
      System.out.print("Entrer n=");
      n = TC.lireInt();
      if(! TC.eof())
          System.out.println("n^2=" + (n*n));
  } while(! TC.eof());

qui teste explicitement si l'on est arrivé à la fin du fichier (ou si on a quitté le programme).

Lectures de fichier

Supposons que le fichier fic.int contienne les entiers suivants :
1 2 3 4
5
6
6
7
et qu'on veuille récupérer un tableau contenant tous ces entiers. On utilise :
  int[] tabi = TC.intDeFichier("fic.int");

  for(int i = 0; i < tabi.length; i++)
      System.out.println(""+tabi[i]);

On peut lire des double par :
  double[] tabd = TC.doubleDeFichier("fic.double");

  for(int i = 0; i < tabd.length; i++)
      System.out.println(""+tabd[i]);

La fonction
    static char[] charDeFichier(String nomfichier)
permet de récupérer le contenu d'un fichier dans un tableau de caractères. De même, la fonction
    static String StringDeFichier(String nomfichier)
retourne une chaîne qui contient le fichier nomfichier.

On peut également récupérer un fichier sous forme de tableau de lignes à l'aide de :
    static String[] lignesDeFichier(String nomfichier)
La fonction
    static String[] motsDeFichier(String nomfichier)
retourne un tableau de chaînes contenant les mots contenus dans un fichier, c'est-à-dire les chaînes de caractères séparées par des blancs, ou bien des caractères de tabulation, etc.

Sortie dans un fichier

Il s'agit là d'une fonctionnalité spéciale. En effet, on souhaite pouvoir faire à la fois une sortie d'écran normale, ainsi qu'une sortie simultanée dans un fichier. L'utilisation typique en est la pale machine.

Pour bénéficier de cette fonctionnalité, on utilise:
    TC.sortieFichier("nom_sortie");
    TC.print(3);
    TC.println(" allo");
ce qui a pour effet d'afficher à l'écran
3 allo
ainsi que d'en faire une copie dans le fichier nom_sortie. Les fonctions TC.print et TC.println peuvent être utilisées en lieu et place de System.out.print et System.out.println.

Si on oublie de faire l'initialisation, pas de problème, les résultats s'affichent à l'écran comme si de rien n'était.

Conversions à partir des String

Souvent, on récupère une chaîne de caractères (par exemple une ligne) et on veut la couper en morceaux. La fonction
    static String[] motsDeChaine(String s)
retourne un tableau contenant les mots de la chaîne.

Si on est sûr que s ne contient que des entiers séparés par des blancs (cf. le chapitre sur les polynômes), la fonction
    static int[] intDeChaine(String s)
retourne un tableau d'entiers contenus dans s. Par exemple si s="1 2 3", on récupèrera un tableau contenant les trois entiers 1, 2, 3 dans cet ordre. Si l'on a affaire à des entiers de type long, on utilisera :
    static long[] longDeChaine(String s)

Utilisation du chronomètre

La syntaxe d'utilisation du chronomètre est la suivante :
  long t;

  TC.demarrerChrono();
  N = 1 << 10;
  t = TC.tempsChrono();
La variable t contiendra le temps écoulé pendant la suite d'opérations effectuées depuis le lancement du chronomètre.

A.3.2  La classe Efichier

Cette classe rassemble des primitives de traitement des fichiers en entrée. Les explications qui suivent peuvent être omises en première lecture.

L'idée est d'encapsuler le traitement des fichiers dans une structure nouvelle, appelée Efichier (pour fichier d'entrées). Cette structure est définie comme :
class Efichier{
    BufferedReader buf;
    String ligne;
    StringTokenizer tok;
    boolean eof, eol;
}

On voit qu'elle contient un tampon d'entrée à la Java, une ligne courante (ligne), un tokeniser associé (tok), et gère elle-même les fins de ligne (variable eol) et la fin du fichier (variable eof). On a défini deux constructeurs :

    Efichier(String nomfic){
        try{
            buf = new BufferedReader(new FileReader(new File(nomfic)));
        }
        catch(IOException e){
            System.out.println(e);
        }
        ligne = null;
        tok = null;
        eof = false;
        eol = false;
    }

    Efichier(InputStreamReader isr){
        buf = new BufferedReader(isr);
        ligne = null;
        tok = null;
        eof = false;
        eol = false;
    }
le second permettant d'utiliser l'entrée standard sous la forme :


static Efichier STDIN = new Efichier(new InputStreamReader(System.in));
La lecture d'une nouvelle ligne peut alors se faire par :
    String lireLigne(){
        try{
            ligne = buf.readLine();
        }
        catch(EOFException e){
            eol = true;
            eof = true;
            return null;
        }
        catch(IOException e){
            erreur(e);
        }
        eol = true;
        if(ligne == null)
            eof = true;
        else
            tok = new StringTokenizer(ligne);
        return ligne;
    }

où nous gérons tous les cas, et en particulier eof ``à la main''. On définit également lireMotSuivant, etc.

Les fonctionnalités de gestion du terminal sont encapsulées à leur tour dans la classe TC, ce qui permet d'écrire entre autres :
class TC{
    static boolean eof(){
        return Efichier.STDIN.eof;
    }

    static int lireInt(){
        return Efichier.STDIN.lireInt();
    }
...
}


1
Ce passage peut être sauté en première lecture.

Précédent Remonter Suivant