Exercices

blocs try/catch et déroulement des instructions

Ecrire une classe TestURL avec un simple main (pas de package nécessaire)

Utilisez les arguments d’appels du programme pour créer des objets java.net.URL (utiliser ce constructeur)

  • Si l’URL est correctement formée (un protocole existant) afficher un message. Ensuite tenter de se connecter à cette URL en utilisant la méthode openStream()

  • Si on obtient la connexion afficher un message

  • Sinon afficher l’exception correspondante

(autre option: écrire en Jshell une méthode void testURL(String… args) et la tester directement)

NOTE IMPORTANTE : si vous êtes derrière un proxy utiliser les "propriétés" suivantes passées à la machine virtuelle : java -Dhttp.proxyHost=hote -D http.proxyPort=port TestURL. (on peut également avoir besoin des propriétés http.proxyUser et http.proxyPassword.). Une autre possibilité est d’avoir un système qui donne la configuration du proxy (ProxySelector) dans ce cas utiliser la propriété: -Djava.net.useSystemProxies (éventuellement fixer ces propriétés dans jre/libs/net.properties)

Une solution (noter l’enchainement des instructions)

Utilisation d’exceptions

  • (facultatif) Modifer la classe Voiture: assurez-vous que le prix est toujours positif et que le modèle n’est pas null.

    Quel genre d’exceptions allez vous utiliser?

    Modifier la classe VoitureAncienne en conséquence.

  • Quand on gare un Voiture s’assurer que le Garage n’est pas plein. De quel genre d’Exception a t’on besoin? Ecrire le code correspondant.

  • Ecrire un test.

Un code pour le Garage (test inclus dans le code)

Définition et utilisation d’exception

(note: l’exercice comporte des simplifications de réalisation: dans un contexte plus "réel" les choix en serait discutables)

Objet: réalisation de classes pour un planning.

Dans un objet Journee on fixe des Taches.

Un objet Tache a une heure de début, une heure de fin et un "objet" (type String).

Pour différentes raisons les Heures sont représentées par un énuméré:

public enum HeureOuvrable {
   _8_H, _9_H, _10_H, _11_H, _12_H, _13_H, _14_H, _15_H, _16_H, _17_H, _18_H ;

}

On aura donc un constructeur de Tache comme ceci :

public Tache(HeureOuvrable début, HeureOuvrable fin, String objet) {
}
  • écrire le code de ce constructeur en controlant que l’heure de début soit avant l’heure de fin. Pour cela utiliser la méthode compareTo des Enums et en cas d’incident déclencher une Exception ExceptionDefinitionTache.

    Définir cette Exception

    Ecrire le code du constructeur

    Tester

  • écrire le code de la classe Journee. Celle-ci contiendra un tableau de Taches défini comme suit:

    private Tache[] taches = new Tache[EnumSet.allOf(HeureOuvrable.class).size()-1] ;

    définir dans Journee une méthode qui permette de fixer une tache dans le planning de la journée:

    public void add(Tache tâche) .....

    Dans chaque élément du tableau taches on ajoutera une référence vers la Tache qui occupe cette plage horaire. Pour déduire un index à partir d’un objet HeureOuvrable utiliser sa méthode ordinal.

    Si deux taches se retrouvent à la même heure déclencher un Exception ExceptionPlanning que l’on définira.

    Tester.

utilisation d’exceptions dans le code de oubangui.com

(exercice de révision à réaliser ultérieurement)

Quand on rajoute un Livre dans le Panier le code agira sur le stock du Livre (invocation de retirerDuStock(1))

On doit donc tester si le code correspondant dans Livre est capable d’opérer correctement:

  • Définir et écrire une ExceptionStock

  • Dans la classe Livre modifier la méthode retirerDuStock.

  • Dans la classe Panier invoquer cette méthode lorsqu’on ajoute un Livre au panier.

  • Modifier tous les codes qui doivent prendre en compte les Exceptions.

Exemples

blocs try/catch et déroulement des instructions

import java.io.IOException;
import java.net.URL;

public class TestURL {

    // peut également s'écrire comme une fonction JShell
    // void testURL(String... args)
    // avec des appels directs
    public static void main(String[] args) {
        for(String st : args){
            try {
                URL url = new URL(st);
                System.out.println(" URL bien formée :" + st);
                url.openStream();
                System.out.println("connexion ok pour :" + st);
            } catch (IOException exc){
                System.err.println("Exception : " + exc );
            }
        }
    }
}

Utilisation d’exceptions

package com.grandgarage.biz;

import java.awt.Color;
import static java.awt.Color.*;

public class VoitureV4 {
    private String id ;
    private String modèle;
    private double prixHT;
    private Color couleur;
    /*  Pour les besoins de l'exercice seulement
     * en effet le taux de TV peut changer
     * et on ne devrait pas avoir à modifier ce code
     * en pratique ce taux est ce qu'on appelle une "ressource"
     * et il faudra apprendre à la configurer.
     */
    public static double TAUX_TVA = 1.235;

    public VoitureV4(String id, String md, double px) throws IllegalArgumentException {
        this(id, md, px, BLACK);

    }

    /**
     * Exemple de documentation
     * @param md modèle
     * @param px prix
     * @param col peut être null
     * @throws java.lang.IllegalArgumentException si modèle null ou si prix < 0
     */
    public VoitureV4(String id, String md, double px, java.awt.Color col) throws IllegalArgumentException {
        if ((md == null) || (id == null)) {
             // les "RuntimeExceptions" peuvent accepter des messages
            // la chaîne devrait être en fait une clef dans une table de trad.
            // simplification: on devrait faire un diagnostic différencié!
            throw new IllegalArgumentException("pas de nom de modèle ou id");
        }
        this.id = id ;
        // voir commentaires dans la doc de "setPrix"
        this.setPrix(px);
        this.modèle = md;
        this.couleur = col;
    }
    public String getId() {
        return this.id ;

    }

    public String getModèle() {
        return this.modèle;
    }

    public java.awt.Color getCouleur() {
        return this.couleur;
    }

    public double getPrixTTC() {
        return Math.rint(this.prixHT *  this.getTauxTaxe()) ;
    }

    public double getTauxTaxe() {
        return TAUX_TVA;
    }

    /** prixHT est le seul champ modifiable!
     * <BR>
     * Pour différentes raisons cette méthode est "final" :
     * <UL>
     * <LI> c'est probablement sensé au niveau fonctionnel
     * <LI> si une méthode de l'instance courante est appelée depuis
     * le code de construction il est préférable que cette méthode ne soit
     * pas publique (ou qu'elle soit "final").
     * </UL>
     * @param prixHT
     * @throws IllegalArgument exception si prix négatif
     */
    public final void setPrix(double prix) throws IllegalArgumentException {
        if (prix < 0) {
            throw new IllegalArgumentException("prix négatif " + prix);
        }
        this.prixHT = prix;
    }

    /**
     * question fondamentale: qu'est ce qui fait que deux voitures sont "égales"
     * est-ce leur référence (si on ne modélise pas une voiture en particulier)
     * ou une combinaison de différents champs?
     */
    @Override
    public boolean equals(Object autre) {
        return (autre instanceof VoitureV4) &&
                (((VoitureV4) autre).id.equals(this.id));
    }

    @Override
    public int hashCode() {
        // algorithme complexe de Hash (généré par NetBeans)
        // int hash = 97 * 7 +  this.modele.hashCode() );
        return this.id.hashCode();
    }

    public String toString() {
        return this.id + " " + this.modèle ;
    }
}
package com.grandgarage.biz;

import java.awt.Color;
import static java.awt.Color.*;

public class VoitureAncienneV4 extends VoitureV4 {

   private int année;

   public VoitureAncienneV4(String id, String md, double prix, Color col, int année)
      throws IllegalArgumentException {
      super(id, md, prix, col);
      // on pourrait aussi tester la validité de l'année
      // mais parfois le mieux est l'ennemi du bien!
      this.année = année;
   }

   /**
    *  Une logique très complexe: typiquement ceci
    * est un code de déploiement susceptible d'être modifié
    * quand les règles de gestion changent
    */
   @Override
   public double getTauxTaxe() {
      if (année > 1970) {
         return super.getTauxTaxe();
      }
      if (année < 1920) {
         return 1;
      }
      double tauxNormal = super.getTauxTaxe() - 1;
      //
      double ratio = (1970 - this.année) * 0.02;
      return 1 + (tauxNormal * ratio);
   }

   public String toString() {
      return super.toString()
         + " " + this.année;
   }
   //////////////////////////////////////////////////////////

   public static void main(String[] args) {
      VoitureV4 maVoiture = new VoitureV4("56RT34", "verso", 11567.56, GRAY);
      System.out.println(maVoiture.getPrixTTC());
      VoitureAncienneV4 maCharette = new VoitureAncienneV4("45RT34", "datsun", 11567.56, GRAY, 1945);
      VoitureAncienneV4 monAntiquité = new VoitureAncienneV4("45RK34", "micra", 11567.56, GRAY, 1985);
      System.out.println(maCharette.getPrixTTC());
      System.out.println(monAntiquité.getPrixTTC());

   }
}
package com.grandgarage.biz;

import java.util.Arrays;
import static java.awt.Color.*;
import java.util.logging.Level;
import java.util.logging.Logger;

public class GarageC {

   private VoitureV4[] parking;
   private int nbPlacesLibres;

   public GarageC(int taille) {
      // on pourrait controler la taille !
      this.nbPlacesLibres = taille;
      parking = new VoitureV4[taille];
   }

   public int garer(VoitureV4 voiture) throws ExceptionContenance {
      // le parking est-il plein?
      if (this.nbPlacesLibres == 0) {
         throw new ExceptionContenance(this, voiture);
      }
      for (int index = 0; index < parking.length; index++) {
         if (null == parking[index]) {
            parking[index] = voiture;
            this.nbPlacesLibres--;
            return index;
         }
      }
      return -1; // anomalie!!!!
      // devrait etre
      // throw new AssertionError("parking non plein et place non trouvée!");
   }

   /**
    * question: faut-il déclencher une exception si la voiture
    * n'est pas présente?
    * se discute: ici non ...
    * @param voiture
    * @return
    */
   public int retirer(VoitureV4 voiture) {
      for (int ix = 0; ix < parking.length; ix++) {
         if (voiture.equals(parking[ix])) {
            parking[ix] = null;
            nbPlacesLibres++;
            return ix;
         }
      }
      return -1; // considéré comme une anomalie mais pas une erreur
   }

   public double getValeur() {
      double res = 0;
      for (int ix = 0; ix < parking.length; ix++) {
         if (null != parking[ix]) {
            res += parking[ix].getPrixTTC();
         }
      }
      return res;
   }

   public String toString() {
      return Arrays.toString(parking);
   }

   public static void main(String[] args) {
      try {
         GarageC garage = new GarageC(2);
         garage.garer(new VoitureV4("45RT34", "verso", 11567.56, GRAY));
         // polymorphism
         garage.garer(new VoitureAncienneV4("46RT34", "datsun", 11567.56, GRAY, 1945));
         System.out.println(garage.getValeur());
         garage.garer(new VoitureAncienneV4("47RT34", "micra", 11567.56, GRAY, 1985));
         System.out.println(garage.getValeur());
      } catch (ExceptionContenance ex) {
         Logger.getLogger("com.grandgarage.biz").log(Level.SEVERE, "garage plein", ex);
      }
   }
}
package com.grandgarage.biz;

/**
 *
 * exprime un dépassement de taille
 */
public class ExceptionContenance extends Exception {
    // nous verrons ultérieurement comment se sortir de ce mauvais pas
    // Object trop général et Garage induit un couplage!
    public final Object contenant ;
    public final Object objetEntrant ;
    public final long estampille = System.currentTimeMillis();

    public ExceptionContenance(Object contenant , Object entrant ) {
   this.contenant = contenant ;
   this.objetEntrant = entrant ;
    }

    public String toString() {
        return  super.toString() +
      " contenant: " + this.contenant
      + "; entrant : " + this.objetEntrant ;
    }
    /*
       NOTE : un simple code comme celui-ci pourrait suffire
   ne pas négliger la création de classes comme celle-ci
   (et ne pas remplacer ça par un message dans une exception controlée plus générale!)

   public class ExceptionGaragePlein extends Exception {
   }
     */

}

Définition et utilisation d’exception

package org.plannings;

/**
 * très très discutable ... mais c'est un exercice!
 */
public enum HeureOuvrable {
   _8_H, _9_H, _10_H, _11_H, _12_H, _13_H, _14_H, _15_H, _16_H, _17_H, _18_H ;

}
package org.plannings;

public class ExceptionDefinitionTache extends IllegalArgumentException {
   public final HeureOuvrable début ;
   public final HeureOuvrable fin ;
   public final String objet ;

   public ExceptionDefinitionTache(String précision, HeureOuvrable début, HeureOuvrable fin, String objet) {
      super(précision);
      this.début = début;
      this.fin = fin;
      this.objet = objet;
   }
   public ExceptionDefinitionTache( HeureOuvrable début, HeureOuvrable fin, String objet) {
      this("", début, fin, objet) ;
   }

   public String toString() {
      return super.toString()
         + "objet: " + this.objet
         + " [" + this.début
         + " ; " + this.fin + "]" ;
   }



}
package org.plannings;

/**
 *
 */
public class Tache {
   private final HeureOuvrable début;
   private final HeureOuvrable fin ;
   private final String objet ;

   public Tache(HeureOuvrable début, HeureOuvrable fin, String objet) {
      if( début.compareTo(fin)>= 0) {
         throw new ExceptionDefinitionTache(début, fin, objet) ;
      }
      this.début = début;
      this.fin = fin;
      this.objet = objet;
   }

   public String getObjet() {
      return objet;
   }

   public HeureOuvrable getDébut() {
      return début;
   }

   public HeureOuvrable getFin() {
      return fin;
   }

   public String toString() {
      return this.objet + " ["
         + this.début + ";"
         + this.fin + "]" ;

   }
}
package org.plannings;

public class ExceptionPlanning extends Exception {
   public final Tache existant ;
   public final Tache projeté ;

   public ExceptionPlanning(String clefMessage, Tache existant, Tache projeté) {
      super(clefMessage);
      this.existant = existant;
      this.projeté = projeté;
   }

   public String toString() {
      return super.toString()
         + " = existant : " +this.existant
         +" ; projeté : " + this.projeté ;
   }

}
package org.plannings;

import java.util.Arrays;
import java.util.EnumSet;

public class Journee {
   private Tache[] taches = new Tache[EnumSet.allOf(HeureOuvrable.class).size()-1] ;

   public void add(Tache tâche) throws ExceptionPlanning {
      int idxDebut = tâche.getDébut().ordinal() ;
      int idxFin = tâche.getFin().ordinal() ;
      Tache[] plage = new Tache[idxFin - idxDebut] ;
      for(int iPlage=0, iTaches=idxDebut;
         iTaches < idxFin ; iPlage++, iTaches++){
         if(taches[iTaches] != null) {
            throw new ExceptionPlanning("recouvrement" ,taches[iTaches], tâche) ;
         } else {
            plage[iPlage] = tâche ;
         }
      }
      System.arraycopy(plage,0, taches,idxDebut, plage.length) ;
   }

   public String toString() {
      return Arrays.toString(taches) ;
   }
}
package org.plannings;

import static org.plannings.HeureOuvrable.*;

public class TestTaches {

   public static void main(String[] args) {
      //Tache tache = new Tache(_10_H, _9_H, "pas bon") ;
      Journee journée = new Journee();
      try {
         journée.add(new Tache(_9_H, _10_H, "9 à 10"));
         journée.add(new Tache(_11_H, _13_H, "11 à 13"));
         journée.add(new Tache(_15_H, _18_H, "15 à 18"));

         System.out.println("" + journée);
         journée.add(new Tache(_12_H, _14_H, "12 à 14"));
      } catch (ExceptionPlanning exc) {

         System.out.println(exc);
         System.out.println("" + journée);

      }
   }
}

utilisation d’exceptions dans le code de oubangui.com

package com.oubangui.biz2;

/**
 *
 * @author Paul-Bernard.Amade
 */
public class ExceptionStock extends Exception {

    /**
     * NOTE IMPORTANTE: ici il y a un "couplage fort"
     * entre les classes Livre et ExceptionStock (elles se référencent l'une l'autre)
     * Il y a plusieurs manières de se tirer de ce mauvais pas
     * <UL>
     *   <LI> utiliser l'identifiant unique du livre comme référence
     *  <LI> utiliser Object (exception plus générale)
     *  <LI> (Exceptions paramétrées non disponibles pour l'instant)
     *  <LI> meilleure option:utiliser un type interface ... voir plus loin
     *  <LI> créer un type Livre.ExceptionStockLivre qui est spécifique au Livre
     *    (technique des classes enchassées ... voir plus loin...)
     * </UL>
     */
    public final Livre livre;
    /**
     * NOTE IMPORTANTE: l'état du stock est très "fragile" (il évolue en permanence)
     * entre le moment où l'exception est générée et le moment où elle est exploitée
     * cet état peut avoir évoluer. on a donc besoin d'un "instantané"
     */
    public final int étatStock;
    public final int nbDemandé;

    public ExceptionStock(Livre bk, int stock, int req) {
        this.livre = bk;
        this.étatStock = stock;
        this.nbDemandé = req;
    }

    @Override
    public String toString() {
        return "StockException: " + this.livre +
                ";stock= " + this.étatStock +
                ";demandés= " + this.nbDemandé;
    }
}
package com.oubangui.biz2;

/**
 */
public class Livre {
     // MEMBRES :  CHAMPS
      // immuables, obligatoires
   private long nISBN ; //id
   private String titre ;
   private String auteurs ;

      // modifiables , obligatoires
    private double prix ;

      // modifiables, peut être null
   private String description ;

      // donnée technique
   private int stock ;

   //  CONSTRUCTEURS
   /** LE constructeur "minimum" doit initialiser les
         * membres obligatoires
    */
   public Livre (long id, String titre, String auteurs, double prix) {
      this.nISBN = id ;
      this.titre = titre ;
      this.auteurs = auteurs ;
      this.prix = prix ;
   }
   // note : les arguments devraient être testés pour controle
        // de validité
   public Livre (long id, String titre, String auteurs, double prix, int stock){
       this(id, titre, auteurs, prix);
       this.stock = stock ;
   }
   // MEMBRES : METHODES
   // ACCESSEURS ET MUTATEURS
   public long getISBN() { return this.nISBN ; }
   public String getTitre() { return this.titre ; }
   public String getAuteurs() { return this.auteurs ; }

   public double getPrix() { return this.prix ; }
   public final void setPrix(double val) {
      this.prix = val ;
   }

   public String getDescription(){
      return this.description ;
   }
   public void setDescription(String desc) {
      this.description = desc ;
   }

   // MODIFICATEURS, SERVICES
   public void retirerDuStock(int nb)throws ExceptionStock {
     if(nb > this.stock) {
            throw new ExceptionStock(this, this.stock, nb);
      }
      this.stock -= nb ;
   }
   public void rajouterAuStock(int nb) {
      this.stock += nb ;
   }

   public String toString(){
      return this.titre +
               " (" + this.nISBN + ") "
                 + this.auteurs ;
   }

}
package com.oubangui.biz2;

/**
 *
 */
public class Panier {
// MEMBRES :  CHAMPS
      // peut avoir un ID si persistant
      //
      // immuable,  non obligatoire (sauf pour le paiement)
   private Client  client ;

      // données techniques

   //pas terrible: peux s'effondrer
   private Livre[] livres = new Livre[10] ;
   private int nbLivres = 0 ;

   //  CONSTRUCTEURS
   // Constructeur par défaut ici
        // on notera: pas de constructeur avec Client!

   // MEMBRES : ACCESSEURS/MUTATEURS
   public Client getClient() { return this.client ; }
   public void setClient(Client cli) {
      this.client = cli;
   }

   // MODIFICATEURS, SERVICES
   public void ajouter(Livre livre) throws ExceptionStock {
       livre.retirerDuStock(1);
       // ATTENTION: NON EXECUTE EN CAS DE PROPAGATION D'EXCEPTION
       this.livres[this.nbLivres++] = livre ;
                /* une autre version plus explicite
                 * try {
                 *      livre.retirerDuStock(1);
                 *      this.livres[this.nbLivres++] = livre ;
                 * } catch (ExceptionStock exc) {
                 *      // on peut prévenir la gestion de stock
                 *      ExceptionEnglobante ex = new ExceptionEnglobante(°°°) ;
                 *      ex.setCause(exc) ;
                 *      throw ex ;
                 * }
                 */

   }
    @Override
   public String toString(){
            String res = " --- Panier ---\n";
            for(int ix = 0 ; ix < this.nbLivres; ix++){
               res += this.livres[ix].toString() + '\n'  ;
            }
            return res ;

   }
}
package com.oubangui.biz2;

/**
 * Pourrait être un expression JShell
 */
public class TestPanier {
    public static void main(String[] args) throws Exception {
        Panier cart = new Panier();
        cart.ajouter(new Livre(1234L, "L'art de peler les aubergines", "Joël Robuchon", 1345.67,10));
        cart.ajouter(new Livre(45678L, "De Re Coquinaria", "Apulée", .99));
        System.out.println(cart.toString());
    }
}