Cours Java
Cours Java
Cours Java
1
3 3 4 4 6 6 9 9 9 9 10 10 10 10 11 11 12 12
Hritage 2.1 Rednition de fonctions . . . . . . . . . . . 2.2 Implmentation de lhritage . . . . . . . . . 2.3 Type et classes : le polymorphisme . . . . . . 2.4 Classes abstraites . . . . . . . . . . . . . . . 2.5 super et this . . . . . . . . . . . . . . . . 2.5.1 Introduction . . . . . . . . . . . . . . 2.5.2 Dnitions . . . . . . . . . . . . . . 2.5.3 super et this pour qualier des champs 2.5.4 super et this dans les mthodes . . . . 2.5.5 super et this dans les constructeurs . . 2.5.6 this comme argument de mthode . .
Interfaces 13 3.1 Les Interfaces comme spcication . . . . . . . . . . . . . . . . . . . . . . . . . 13 3.2 Les interfaces comme collections de constantes . . . . . . . . . . . . . . . . . . 13 Cast 15 4.1 Cast et types de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 4.2 Cast et classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Lidentit des objets 5.1 Comparaison dobjets : equals() . . . . . . . . . . . . . . . . . . . . . . . . 5.2 Le hashcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3 Duplication dun objet : clone() . . . . . . . . . . . . . . . . . . . . . . . . . 17 17 18 18
ii 6 Exceptions 6.1 Introduction . . . . . . . . . . . . 6.2 Terminologie . . . . . . . . . . . 6.3 Les exceptions en JAVA . . . . . . 6.4 Propagation dexceptions . . . . . 6.5 Interception des exceptions en Java 6.5.1 La clause try...catch . . . . 6.5.2 La clause nally . . . . . 6.5.3 catch et lhritage . . . . . 6.6 Dnition dun type dexceptions
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Threads 7.1 Introduction . . . . . . . . . . . . . . . . . . . . 7.2 Dclaration . . . . . . . . . . . . . . . . . . . . 7.3 Lancement dun thread . . . . . . . . . . . . . . 7.4 Le partage du temps . . . . . . . . . . . . . . . . 7.4.1 Priorits . . . . . . . . . . . . . . . . . . 7.4.2 La mthode yield() . . . . . . . . . . 7.5 Variables partages . . . . . . . . . . . . . . . . 7.5.1 Synchronized . . . . . . . . . . . . . . . 7.5.2 Volatile . . . . . . . . . . . . . . . . . . 7.6 Contrle de lexcution . . . . . . . . . . . . . . 7.6.1 Arrt dun thread . . . . . . . . . . . . . 7.6.2 Attente de la n dun thread . . . . . . . 7.6.3 Mthode sleep . . . . . . . . . . . . . . 7.7 viter les attentes actives avec wait et notify 7.7.1 Introduction . . . . . . . . . . . . . . . . 7.7.2 Mthodes . . . . . . . . . . . . . . . . . 7.7.3 Utilisation de wait et notify . . . . . . . . 7.7.4 Client et serveur sans attente active . . . 7.7.5 Gestion dune le de messages . . . . . . 7.8 Dmons . . . . . . . . . . . . . . . . . . . . . . 7.9 Graphismes . . . . . . . . . . . . . . . . . . . . 7.9.1 synchronisation et graphismes . . . . . .
II Bibliothques particulires
1 Les Collections 1.1 Introduction . . . . . . . 1.2 Itrateurs . . . . . . . . 1.3 Organisation des classes 1.4 Linterface Collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
43
45 45 46 47 49
iii 50 51 51 52 53 53 53 54 57 57 60 60 60 60 61 62 62 63 63 65 65 66 66 67 67 68 68 68 68 68 70 75 75 75 75 76 76 77 77 78 78
Entres/sorties 2.1 La classe File . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Organisation des classes dentres/sortie . . . . . . . . . . . 2.2.1 Fichiers en lecture, chiers en criture . . . . . . . . 2.2.2 Classes orientes octets, classes orientes caractres 2.2.3 Filtrage . . . . . . . . . . . . . . . . . . . . . . . . 2.3 Quelques Classes . . . . . . . . . . . . . . . . . . . . . . . 2.4 Exemples dutilisation . . . . . . . . . . . . . . . . . . . . 2.4.1 Lecture dun chier . . . . . . . . . . . . . . . . . . 2.5 Mthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5.1 Constructeurs . . . . . . . . . . . . . . . . . . . . . 2.5.2 Reader . . . . . . . . . . . . . . . . . . . . . . . . 2.5.3 Writer . . . . . . . . . . . . . . . . . . . . . . . . . 2.6 La classe RandomAccessFile . . . . . . . . . . . . . . . . . 2.6.1 Ouverture et fermeture . . . . . . . . . . . . . . . . 2.6.2 Lecture et criture . . . . . . . . . . . . . . . . . . 2.6.3 Dplacement . . . . . . . . . . . . . . . . . . . . . 2.7 Srialisation . . . . . . . . . . . . . . . . . . . . . . . . . . 2.7.1 Conditions demploi . . . . . . . . . . . . . . . . . 2.7.2 Champs non sauvegards . . . . . . . . . . . . . . . 2.8 La classe StreamTokenizer . . . . . . . . . . . . . . . . . . 2.8.1 Introduction . . . . . . . . . . . . . . . . . . . . . . 2.8.2 Documentation . . . . . . . . . . . . . . . . . . . . Java et les bases de donnes 3.1 Introduction JDBC . . . . . . . . . . . . . 3.2 Architecture . . . . . . . . . . . . . . . . . . 3.3 Un exemple : postgres . . . . . . . . . . . . . 3.4 tablir la connexion . . . . . . . . . . . . . . 3.4.1 Exemple : . . . . . . . . . . . . . . . 3.5 Envoyer une requte . . . . . . . . . . . . . 3.5.1 Mthodes . . . . . . . . . . . . . . . 3.5.2 Mthodes applicables un ResultSet 3.5.3 Execute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
iv 3.6 3.7 3.8 Commandes prpares . . . . . . . . . . . . . . . . . . chappements SQL . . . . . . . . . . . . . . . . . . . . Gestion des transactions . . . . . . . . . . . . . . . . . 3.8.1 Niveau disolement . . . . . . . . . . . . . . . . 3.9 Capacits de la base de donnes : DataBaseMetaData 3.10 Exploration des tables . . . . . . . . . . . . . . . . . . . 3.10.1 mthodes de ResultSetMetaData . . . . . . 3.11 Extensions du jdbc2.0 . . . . . . . . . . . . . . . . . . . 3.11.1 ResultSet navigables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Java Server Pages et Servlets 4.1 Architecture dune application web . . . . 4.2 Introduction aux jsp . . . . . . . . . . . . 4.2.1 principes . . . . . . . . . . . . . 4.2.2 Balises principales des jsp . . . . 4.2.3 Les champs dune jsp . . . . . . . 4.2.4 Installation des jsp . . . . . . . . 4.3 Introduction aux servlets . . . . . . . . . 4.3.1 principe des servlets . . . . . . . 4.3.2 Installation des servlets . . . . . . 4.3.3 Les servlets, leur vie, leur uvre . 4.4 Les javabeans . . . . . . . . . . . . . . . 4.4.1 Les classes de beans . . . . . . . 4.4.2 Accs aux beans depuis les jsp . . 4.4.3 accs aux beans depuis les servlets 4.5 Architecture typique dune application jsp 4.5.1 Redirection vers une jsp . . . . . 4.6 Le suivi des sessions . . . . . . . . . . . 4.7 Cration de nouvelles balises pour les jsp 4.7.1 Une balise simple . . . . . . . . . 4.7.2 Une boucle . . . . . . . . . . . . 4.7.3 Partage de variables . . . . . . . 4.8 Quelques patterns . . . . . . . . . . . . .
Java et XML 5.1 Le langage XML . . . . . . . . . . . . . . 5.1.1 Introduction . . . . . . . . . . . . . 5.1.2 Documents XML . . . . . . . . . . 5.1.3 XSLT . . . . . . . . . . . . . . . . 5.1.4 Schmas XML . . . . . . . . . . . 5.2 Les API Java . . . . . . . . . . . . . . . . 5.2.1 Document object model (DOM) . . 5.2.2 Simple API for Xml Parsing (SAX)
v 110 111 111 111 113 113 113 115 115 115
Swing Avanc 6.1 Patterns en swing : lexemple de JTable . . . . . . . . 6.1.1 Le pattern Observateur . . . . . . . . . . . . . . 6.2 Le pattern commande en swing : actions et edits . . . . . 6.2.1 Actions et le pattern commande . . . . . . . . . 6.2.2 Undo, classes textes et design pattern commande
vi
Chapitre 1 Spcicateurs
Un certain nombre de mots clefs permettent de spcier le comportement dun champ ou dune mthode. Nous dtaillerons ici final, static, ainsi que les oprateurs de visibilit public, private, protected.
1.1
Le mot clef nal se place dans la dclaration dun champ, dune classe ou dune mthode.Il indique que llment dclar ne peut tre modi. Dans le cas dun champ, il permet donc de dnir des constantes. On lutilise gnralement avec static (voir 1.2). Exemple :
class Cercle { private double x,y,r; static final double PI= 3.14; ... public double getPerimetre() { return 2*PI*r; } }
Il est aussi possible de lutiliser si la valeur dun champ est xe pour un objet donn. Par exemple, un objet de classe String nest pas modiable. Une dnition possible de cette classe serait donc :
public class MyString { private final char rep[]; public MyString(char t[]) { // Initialisation de rep rep= new char[t.length]; for (int i= 0; i< t.length; i++) rep[i]= t[i]; }
4
... }
CHAPITRE 1. SPCIFICATEURS
Notez que le nal de lexemple prcdent ninterdit pas de changer la valeur dune des cases du tableau rep. Il interdit uniquement faire pointer rep sur un autre tableau. Ainsi, le code suivant ne compilera pas :
public class MyString { ... public void set(char t[]) { rep= t; // Erreur la compilation : // "Cant assign a second value to a blank final variable: rep" } }
Dans le cas dune mthode, final interdit la rednition de la mthode par hritage. Dans le cas dune classe il interdit den driver des sous-classes.
1.2
Le mot cl static
Le mot-cl static vient du C. Il caractrise dans ce langage des variables dont la caractristique la plus marquante est de nexister quen un seul exemplaire. En JAVA, le mot-cl static indique quun lment dune classe est li la classe et non un objet particulier.
Le numro dun objet tudiant est li cet objet. Si nous considrons deux objets tudiants diffrents, e1 et e2, leurs numros seront deux donnes distinctes :
e1= new Etudiant("Turing"); e1.setNumero(0); e2= new Etudiant("Babbage"); e2.setNumero(1);
e1
nom numero 0 Turing
e2
nom numero 1
Babbage
lespace mmoire correspondant e1 et celui correspondant e2 contiennent chacun une place pour le numro et une place pour le nom, comme le montre la gure 1.1. Dans le cas de champs statiques, lespace mmoire pour le champ est rserv indpendamment des objets (gure 1.2). Il existe donc mme si aucun objet de la classe nexiste ; de plus il existe en un seul exemplaire. On y accde en crivant
NomDeLaCLASSE.nomDuChamp
Utilisation dun champ statique : Un champ statique sert gnralement pour conserver des caractristiques de la classe toute entire. Il sagit souvent de constantes (champs final static). Il peut cependant sagir de vritables variables. Par exemple, supposons que nous crivions dune classe Etudiant (au hasard) et que nous voulions que le numro dtudiant soit automatiquement incrment chaque nouvel tudiant cr. Nous pourrions crire :
public class Etudiant { static int maxnum= 0; int numero; String nom; ... public Etudiant(String nom) { numero= maxnum; maxnum= maxnum+1; this.nom= nom; } }
Remarquons cependant quil serait bien meilleur de crer une classe pour grer les numros dtudiants.
CHAPITRE 1. SPCIFICATEURS
e1
nom numero 0 Turing
e2
nom numero 1
Babbage nom et numero ne sont pas statiques maxnum est par contre un champ statique.
Etudiant
maxnum 2
Dans les bibliothques mathmatiques de Java, les fonctions usuelles sont dclares statiques ; en effet, elles ne sappliquent pas un objet :
x= Math.cos(1.4);
Lorsquun programme Java est lanc, il nexiste a priori aucun objet, donc aucun lment auquel appliquer une mthode. Cest pour cela que les concepteurs du langage ont dcid que le corps dun programme serait constitu par une mthode statique : main.
1.3
public le champ est accessible de partout ; en pratique, rservez ce statut aux mthodes. La seule exception raisonnable pour les champs, ce sont les constantes. private le champ nest accessible que depuis la classe elle-mme. Si vous navez pas une excellente raison de donner un autre statut un champ ou une mthode, choisissez private.
(rien) le champ est accessible depuis toutes les classes du package ; a nest pas une raison pour en abuser :-). En pratique, il arrive parfois que plusieurs classes soient intimement lies. Typiquement, elles nont pas de sens les unes sans les autres. Dans ce cas, ce type daccessibilit est envisageable (voir par exemple 1.9.1 pour un usage possible). protected un champ protected dune classe A nest accessible que depuis le package et les classes drives de A. Typiquement, la classe A dnit une fonctions ou des champs qui seront utiles aux classes drives, et seulement celles-ci.
CHAPITRE 1. SPCIFICATEURS
Chapitre 2 Hritage
Lhritage est un mcanisme qui permet dtendre une classe en lui ajoutant des possibilits ou en rednissant certaines des fonctionnalits existantes. Concrtement, tant donn une classe A, lhritage permet de crer une classe B, qui a toutes les caractristiques de la classe A, plus ventuellement de nouveaux champs et de nouvelles mthodes.
Introduisons quelques dnitions 1 : type un objet a un type et un seul : celui qui lui est donn par son constructeur. classe un objet peut par contre avoir plusieurs classes : celle correspondant son type, ainsi que toutes les sur-classes de celle-ci. Supposons que la classe Etudiant hrite de Personne qui hrite de Object. Alors :
Personne e= new Etudiant("toto");
cre un objet de type Etudiant, qui appartient aux classes Etudiant, Personne et Object. Par contre e est de classe Personne, ce qui est lgal car cest bien une des classes de lobjet. rednition (overriding) cest le fait de rcrire dans une classe B une mthode dune des classes dont B hrite. Lorsque cette mthode sera appele sur un objet de type B, alors cest la mthode rednie qui sera utilise et non la mthode de la classe anctre.
1. Lusage des mots type et classe dans ce sens nest notre connaissance pas standard. Cette terminologie provient du livre Design Patterns.
10
CHAPITRE 2. HRITAGE
surcharge (overloading) ne pas confondre avec le prcdent. cest le fait dutiliser le mme nom pour des mthodes diffrentes. Quand les mthodes sont dans la mme classe, elles sont distingues par leurs arguments. Notez que la surcharge na aucun rapport avec lhritage 2 . polymorphisme
2.4 2.5
2.5.1 Introduction
En Java, il arrive parfois quune variable en cache une autre. Trs classiquement, considrons une classe Etudiant et un de ses constructeurs possibles :
public class Etudiant { String nom; public Etudiant(String nom) { ??? } }
Dans le corps du constructeur, le paramtre nom cache le champ (ou variable dinstance) nom . Dans 99 % des langages de programmation, le problme est rsolu en changeant le nom du paramtre. Cest l quintervient this. this signie lobjet auquel sapplique la mthode , ou, dans le cas dun constructeur, lobjet en train dtre construit . Dans ce cas, lcriture this.nom nest pas ambigu, et dsigne bien le champ nom de lobjet de classe Etudiant que nous construisons . Cette fonction, pour tre trs utilise en Java, nen est pas moins secondaire : on pourrait fort bien sen passer. Il est par contre des fois o lemploi de this ou de son compre super est primordial.
2.5.2 Dnitions
this signie lobjet auquel sapplique la mthode , ou, dans le cas dun constructeur, lobjet en train dtre construit ; super Semploie dans le cas dune classe B qui tend une classe de base A. Il signie lobjet auquel sapplique la mthode , ou, dans le cas dun constructeur, lobjet en train dtre construit , mais vu comme un objet de classe A.
2. La surcharge en java est un concept simple. En C++, nous verrons quelle est bien plus complexe, mais aussi plus puissante
11
super : si une classe lle dnit un champ dont un homonyme existe dans une classe parente, ce dernier est masqu. Soit par exemple le code :
class A { public int a; } class B extends A { public String a; ... }
le champ a de type String de B masque le champ int de A. Le mot-clef super permet de faire rfrence, dans une mthode de la classe B, un objet de classe B en le considrant comme un objet de la classe parente, cest--dire A. Ainsi :
class B extends A { String a; ... public void afficher() { System.out.println(a); // affiche le champ String a System.out.println(super.a); // affiche le champ int a // hrit de A. } }
Notez que cet utilisation de super est trs rare. En effet, il est formellement dconseill daccder directement aux champs dune classe. Normalement, on aurait d crire des accesseurs.
CHAPITRE 2. HRITAGE
cest parfaitement inutile. super : lide est toujours la mme. Supposons que nous ayons :
class Personne { ... public void afficher() { // affiche les donnes relative une personne : // son nom, son prnom, son adresse. ... } } class Etudiant extends Personne { ... public void afficher() { // affiche les donnes relative un tudiant : // dabord les donnes existantes dans la classe Personne, // puis celles spcifiques aux tudiants. } }
Il serait intressant de pouvoir utiliser la mthode afficher de Personne pour crire celle de Etudiant.
2.5.5 super et this dans les constructeurs 2.5.6 this comme argument de mthode
13
Chapitre 3 Interfaces
3.1 Les Interfaces comme spcication
Le mcanisme de lhritage en Java est limit lhritage simple. Une classe a une parente directe et une seule (sauf Object, qui nen a pas du tout). Lhritage a un sens trs fort : B hrite de A signie tout B est un A . Or, il arrive que lon veuille simplement signier quune classe sait faire telle ou telle chose. Cest le sens des interfaces. Une interface est principalement un ensemble den-ttes de mthodes. Une classe peut implmenter une ou plusieurs interfaces, cest dire fournir le code correspondant aux mthodes de linterface.
3.2
Les interfaces peuvent aussi contenir des constantes. Il suft alors dimplmenter linterface pour pouvoir les utiliser. Considrons par exemple linterface suivante :
public interface CodesJours { int DIMANCHE= 0; int LUNDI= 1; int MARDI= 2; int MERCREDI= 3; int JEUDI= 4; int VENDREDI= 5; int SAMEDI= 6; }
Notez que ces champs sont en ralit static et final, mais quil est optionnel de lcrire. Toute classe qui implmente CodesJours dispose directement des constantes quelle dnit :
class Calendrier implements CodesJours { ... initWeekEnd() {
14
// Notez que SAMEDI est utilis directement jour[SAMEDI].setRepos(true); jour[DIMANCHE].setRepos(true); } ... }
CHAPITRE 3. INTERFACES
15
Chapitre 4 Cast
Lopration de conversion (casting) couvre en Java deux concepts diffrents. Elle se comporte en effet diffremment sur les types de base, o il sagit dune vritable conversion, et sur les classes, o cest plutt une opration syntaxique.
4.1
Les divers types de base (int, double, etc...) sont plus ou moins compatibles entre eux. On conoit par exemple quil soit logique de pouvoir crire un entier dans un double. Java permet dailleurs cette opration sans autre forme de procs :
int i= 1; double y= i;
Par contre, certaines conversions causent une perte dinformation. crire un double dans un int, cest en perdre la partie dcimale. De mme, crire un int dans un short (entier court) peut conduire une perte de donnes. En Java, les conversions avec perte de donne potentielle sont possibles, mais, contrairement aux conversions sans perte de donnes, elles doivent tre explicitement marque par lopration de casting. Un cast permet de convertir une donne dun type de base en un autre. Il scrit en faisant prcder la valeur convertir par le nom du type cible de la conversion, crit entre parenthses :
double y= 1.3; int j= (double)y;
4.2
Cast et classes
16
CHAPITRE 4. CAST
17
5.1
La mthode equals est hrite par toutes les classes. La classe Objet dnit equals comme :
public boolean equals(Objet o) { return this == o; }
Cest donc initialement un quivalent de ==. Pour les classes qui veulent disposer dune dnition diffrente de lgalit, on doit rednir equals. Cest ce que fait, par exemple, la classe String. Exemple de rednition :
class Entier { int val; Encapsulation dun entier
public Entier(int v) {val= v;} public // // if { boolean equals(Object o) { On compare un Entier et un Object. Ils peuvent tre de type diffrent : (o instanceof Entier) // o est un Entier. Le rcuprer comme tel // grce un cast : Entier e= (Entier)o; return (e.val == val);
18
5.2
Le hashcode
Si une classe rednit equals, elle doit aussi rednir la mthode hashcode : public int hashcode () fonction renvoyant un entier, utilise par les classes comme Hashtable, HashMap et HashSet. La contrainte est que si deux objets sont gaux au sens de equals, ils doivent avoir le mme hashcode (la rciproque ntant pas forcment vrie : deux objets peuvent avoir le mme code, mais tre diffrents). Par contre, plus le hashcode diffrencie des objets diffrents, plus il est efcace. Notez quune fonction constante remplit les conditions (mais ne fournit pas un bon hashcode. Si hashcode et equals sont en dsaccord, les classes telles que HashTable, qui utilisent le hashcode, donneront des rsultats errons.
5.3
rdiger
public class Etudiant implements Cloneable { private String nom; protected Object Clone() { try { Etudiant e = (Etudiant)super.clone(); e.nom = (String)nom.clone(); return e;
19
Important clone() ne peut ni utiliser un constructeur de la classe cloner, ni appeler new pour construire son rsultat.
20
21
Chapitre 6 Exceptions
BON EXEMPLE POUR LES EXCEPTIONS : la classe Note.
6.1
Introduction
Dans de nombreux langages de programmations, comme C, la gestion derreurs est un domaine dlicat et, disons-le, rebutant. Cest sans doute la raison pour laquelle de nombreux programmeurs la ngligent, ce qui peut aboutir des problmes graves. Ainsi, par exemple, la plupart des failles de scurits dans les systmes UNIX sont causes par lexploitation dune gestion derreur dfectueuse. Pourquoi la gestion derreur pose-t-elle des problmes? Considrons lexemple suivant : soit un programme qui ouvre un chier, y lit trois entiers, puis le ferme. Lalgorithme est donc le suivant :
f= ouvrir_fichier(nom); i1= lire_entier(f); i2= lire_entier(f); i3= lire_entier(f); fermer f;
La lecture de ce code ne pose aucun problme, et lon voit tout de suite ce quil fait. Malheureusement, chaque tape, un problme peut se poser : le chier peut ne pas exister ou ne pas tre consultable. Dautre part, il peut contenir des donnes errones, et la lecture de i1, i2 et i3 peut chouer. Regardons quoi correspondrait un systme grant les erreurs :
si ((f= ouvrir_fichier(nom)) == 0) affiche "fichier non ouvert" exit 1 i1= lire_entier(f); si erreur_de_lecture affiche "fichier non ouvert" exit 1 i2= lire_entier(f); si erreur_de_lecture
22
affiche "fichier non ouvert" exit 1 i3= lire_entier(f); si erreur_de_lecture affiche "fichier non ouvert" exit 1 fermer f;
CHAPITRE 6. EXCEPTIONS
On saperoit que ce code est plus lourd, et bien moins lisible que le code prcdent. Le mcanisme des exceptions permet de sparer le code qui dcrit le droulement normal du programme et le code de gestion derreur. Cest ce que fait linstruction try ... catch :
try code dcrivant le droulement normal du programme catch type derreur 1 traitement de lerreur 1 catch type derreur 2 traitement de lerreur 2
Le principe est le suivant : ouvrir_fichier ou lire_entier, si elles chouent, lvent une exception, cest dire envoient un message derreur. Cela interrompt le programme en cours. Par exemple, si la lecture de i1 choue, les lignes suivantes ne seront pas excutes. Au lieu de cela, le contrle passe aux blocs catch qui suivent le try, et le code correspondant lexception leve (dans notre exemple, entier non lu) est excut. Propagation : rien noblige, de plus, grer une erreur dans la fonction mme o elle se produit. Si une exception nest pas traite dans la mthode o elle est leve, elle se propage, en remontant le l des appels de mthodes. En n de compte, si elle nest jamais traite, elle interrompt lexcution du programme.
6.2
Terminologie
Exception : objet reprsentant un message derreur ; Leve dune exception : envoi dun message derreur, qui interrompt le cours normal du programme ;
23
Propagation dune exception : mcanisme par lequel une exception est transmise de fonction en fonction ; Traitement dune exception : interruption de lexception, permettant ventuellement de reprendre le cours du programme.
6.3
Certaines mthodes peuvent dclencher des exceptions. La documentation de lAPI java lindique alors :
public static int parseInt(String s) throws NumberFormatException
un appel de parseInt peut lever une exception si la valeur de s nest pas correcte Le programmeur qui appelle une telle mthode doit prciser sil intercepte lexception ou la laisse se propager.
6.4
Propagation dexceptions
Quand un programmeur appelle une mthode qui peut dclencher une exception, et quil dcide de ne pas intercepter celle-ci, il doit le prciser. Si, dans une mthode, mettons lireFichier(), on appelle la mthode parseInt voque prcdemment, il est
6.5
6.6
* exceptions : try... catch, throws, throw - organisation des exceptions By convention, class Throwable and its subclasses have two constructors, one that takes no arguments and one that takes a String argument that can be used to produce an error message. try int a[] = new int[2]; a[4]; catch (ArrayIndexOutOfBoundsException e) System.out.println("exception: " + e.getMessage()); e.printStackTrace(); Error : non rattrapables
24
CHAPITRE 6. EXCEPTIONS
Exception : The class Exception and its subclasses are a form of Throwable that indicates conditions that a reasonable application might want to catch. RuntimeException : Ces exceptions nont pas forcment besoin dtre rattrapes. Error LinkageError NoClassDefFoundError Exception IOException EOFException FileNotFoundException RuntimeException ArithmeticException NumberFormatException IndexOutOfBoundsException ArrayIndexOutOfBoundsException StringIndexOutOfBoundsException NullPointerException - liste de catch - nally
25
Chapitre 7 Threads
7.1 Introduction
Dnition : Si plusieurs parties dun programme sexcutent en mme temps (ou semblent sexcuter en mme temps), on dit quelles sexcutent en parallle. Chaque excution de partie de programme est appele un thread (ou l dexcution). Les threads permettent donc dcrire des programmes multitches. Par dfaut, un programme Java est compos dun seul Thread. la diffrence des processus dUnix, les threads dun mme programme partagent le mme espace mmoire, et communiquent facilement entre eux.
7.2
Dclaration
Il existe deux mthodes pour excuter des threads. Une premire mthode est dtendre la classe Thread. Dans ce cas, il faut rednir la mthode run. Au lancement du thread, cest cette mthode qui sera excute.
// Thread hritant de la classe thread : public class T1 extends Thread { volatile private T2 k; public void setK(T2 k) { this.k= k; } // Ce thread affiche de manire rpte la valeur de // la variable k public void run() { while (true) { System.out.println(k); try {
26
// Attend une milliseconde sleep(1); } catch (InterruptedException e) {} } } }
CHAPITRE 7. THREADS
La seconde mthode est dimplmenter linterface Runnable. Comme prcdemment, il sagit en fait dcrire une mthode run()
public class T2 implements Runnable { private int nb= 0; // Compte jusqu 1000000 public void run() { while (nb < 100000000) { nb++; } } public String toString() { return "" + nb; } }
7.3
Un thread se lance grce la mthode start(). Une fois le thread lanc, sa mthode run() sexcute. Lorsque cette mthode se termine, le thread meurt. Un thread dni par hritage (premire mthode) se lance de la manire suivante :
// Cration dun objet thread : MonThread t1= new MonThread(); // Pour linstant, rien ne se passe. // On lance le thread : t1.start();
Pour lancer un thread dni par implmentation (seconde mthode), il faut dclarer deux objets : un objet o1 de la classe implmentant Runnable, et lautre de classe Thread. Ce dernier prendra o1 comme paramtre de son constructeur. Il suft ensuite dappeler la mthode start() du thread.
// On cre un objet de la classe implmentant Runnable : MonRunnable monrun= new MonRunnable(); // On cre un thread Thread secondThread= new Thread(monrun); // On lance le thread secondThread.run();
27
Une fois les threads lancs, leur excution se fait en parallle. Notez que le rsultat dune telle excution nest pas dterministe, et dpend de la machine virtuelle java. Voici un exemple utilisant les deux classes dnies prcdemment :
public class Th1 { public static void main(String arg[]) { T1 t1= new T1(); T2 t2= new T2(); Thread t3= new Thread(t2); t1.setK(t2); t1.start(); t3.start(); while (true) { System.out.println("ici main"); try { Thread.currentThread().sleep(10000); } catch (InterruptedException e) {} } } }
7.4
Le partage du temps
Sur la plupart des systmes, le multitche est simul. Sur un systme monoprocesseur, un instant t donn, un seul thread est en train de tourner. Pour donner limpression du multitche, un systme comme comme Unix interrompt assez souvent le processus qui tourne , puis choisit un nouveau processus, qui tourne son tour pendant quelques millisecondes, et ainsi de suite. On appelle ordonnanceur (en anglais : scheduler) le programme qui choisit le processus qui va tourner.
7.4.1 Priorits
Les priorits aident lordonnanceur de la machine virtuelle java choisir le thread qui va tourner. Parmis tous les threads candidats, java choisit lun de ceux qui ont la priorit la plus
28
CHAPITRE 7. THREADS
importante. La priorit est un entier entre 0 et 10, associ un thread. On peut la consulter et la modier grce deux mthodes de Thread, getPriority et setPriority. int getPriority () Rcupre la priorit actuelle de lobjet thread auquel la mthode est applique. void setPriority (int pri) throws SecurityException, IllegalArgumentException xe la priorit de lobjet thread auquel la mthode est applique. la valeur de pri doit tre comprise entre Thread.MIN_PRIORITY (0) et Thread.MAX_PRIORITY (10).
7.5
Variables partages
Comme nous lavons dit en introduction, la grande diffrence entre les threads et les processi Unix rside dans la gestion de la mmoire. La mmoire dun processus Unix est a priori inaccessible depuis les autres processi. Grce cela, un programme Unix mal crit ne peut pas polluer lespace mmoire des autres programmes. De plus, la n dun programme Unix, toute la mmoire utilise est libre. Par contre, comme les espaces mmoire sont spars, il nest pas possible de les utiliser pour communiquer entre processi 1
1. Il est cependant possible dutiliser de la mmoire partage, mais il faut le demander explicitement.
29
Lespace mmoire utilis par les threads est, quand lui, partag. Tous les threads dun mme programme peuvent potentiellement modier et lire toutes les variables utilise dans le programme. Cela permet plusieurs threads de communiquer entre eux par le biais des variables, en modiant ou en lisant leurs valeurs (voir le dernier exemple de 7.3, dans lequel les deux threads t1 et t3 lisent et modient la variable t2.nb). Lennui, cest que laccs en parallle une mme ressource pose des problme thoriques. Considrons les deux pseudo-codes suivants :
1 2 3
thread 1 a= x; a++; x= a;
1 2 3
thread 2 b= x; b--; x= b;
Supposons que ces deux threads sexcutent en parallle, et que x soit une variable partage entre les deux. Supposons de plus que la valeur initiale de x soit 0. Si le thread 1 sexcute dabord, et le thread 2 ensuite, x vaudra 0 au nal. Supposons par contre le comportement suivant (1.3 dsigne la ligne 3 du thread 1) :
1.1 2.1 1.2 2.1 1.3 2.3 a= x; b= x; a++; b--; x= a; x= b;
Alors, x vaudra -1. En supposant que, par contre, 1.3 sexcute aprs 2.3, x vaudra +1. Bref, selon le comportement du programme, qui est absolument imprvisible, x vaudra soit 0, soit 1, soit -1. Ce comportement alatoire a peu de chance de correspondre ce que recherchait le programmeur. La solution utilise est de garantir que certaines portions de code ne seront pas excutes en parallle. On considre la variable x comme une ressource. Lors dune criture, un seul processus doit y avoir accs.
7.5.1 Synchronized
La dclaration synchronised permet de verrouiller un objet : si deux fonctions sont synchronises sur un objet, elles ne pourront sexcuter en parallle. Il existe deux possibilits dutilisation de synchronized : lune dans le corps du code, lautre dans une dclaration de mthode. Code synchronized La dclaration synchronized permet de verrouiller un objet. Elle a la forme :
synchronized (obj) { code ..... }
30
CHAPITRE 7. THREADS
Son effet est le suivant : pour excuter le code, la mthode doit verrouiller lobjet. Si celui-ci nest pas dj verrouill, tout va bien. Sinon, lexcution du thread est suspendue jusqu ce que lobjet soit libr par lautre thread qui lutilisait en le verrouillant. ce moment, un des threads qui veulent verrouiller lobjet obtient le verrou, et les autres threads qui veulent un verrou restent suspendus. Pour rendre dterministe le code prcdent, il sufrait donc dcrire :
1 2 3 4 5
1 2 3 4 5
Mthode synchronized On peut aussi synchroniser le code dune mthode toute entire. Dans ce cas, lobjet verrouill est celui auquel la mthode sapplique. Cela scrit :
public void synchronized increment() { int a= getValue(); a++; setValue(a); }
Fonctionnement de synchronized chaque objet Java est associ un verrou. Une mthode ou un bout de code synchronized doit obtenir ce verroux pour tourner. Une fois que le verroux est obtenu, il est conserv jusqu la n de la mthode ou de la section synchronized. Si un thread essaie dexcuter du code synchronized alors que le verroux ncessaire nest pas disponible, le thread est mis en attente. Lorsque le verroux sera disponible, il pourra peut-tre lobtenir. En pratique, dans la plupart des cas, si un objet est utilis par plusieurs threads la fois, il vaut mieux que les mthodes de cet objet soient synchronized. Si cela nest pas le cas, on peut utiliser des blocs synchronized ou alors encapsuler la classe relle de lobjet dans une classe dont les mthodes seront synchronises.
7.5.2 Volatile
Le compilateur peut conserver une copie locale dune variable, pour des raisons defcacit. Normalement, on na pas de problme si on verrouille les variables correctement. Quand une variable est volatile, toute modication ou consultation est effectivement ralise sur la variable partage. class Test {
31
static volatile int i = 0, j = 0; static void one() { i++; j++; } static void two() { System.out.println("i=" + i + " j=" + j); } } } Si one() et two() sont excutes concurremment, sans volatile , two() peut ventuellement observer des cas o j serait plus grand que i. (par exemple, si la valeur de j est rcupre, mais pas celle de i). Avec volatile, non. Cependant, la seule mthode vraiment sre est de passer par synchronized !
7.6
Contrle de lexcution
// Thread hritant de la classe thread : class T4 extends Thread { boolean continuer= true; public synchronized void arreter() { continuer= false; } public synchronized boolean getContinuer() { return continuer; } public void run() { int k=0; while (getContinuer()) { System.out.println("hello"); yield(); } } } public class Th3 { public static void main(String arg[]) { T4 t= new T4(); t.start(); while (true) { System.out.println("ici main"); try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) {}
32
t.arreter(); } } }
CHAPITRE 7. THREADS
Le thread t tourne tant que sa variable interne continuer est true. En la mettant false, on arrte t. Notons cependant que cet arrt nest pas brutal. Il se produit uniquement quand le test de la boucle est atteint.
Cependant, cette version impose aux joueurs des contraintes articielles : le joueur 0 sidentie forcment avant le joueur 1, qui lui-mme sidentie forcment avant le joueur 2, etc... Si lidentication demande une intraction forte entre les clients et le serveur, cette solution nest pas intressante. On pourrait donc faire identier chacun des joueurs en parallle dans des threads :
ThreadIdent t[]= new ThreadIdent[nbrJoueurs]; pour j= 1 jusqu nbrJoueurs t[i]= new ThreadIdent(i); t[i].start();
Mais alors, il est ncessaire pour commencer jouer dattendre que tous les threads de t soient termins :
pour j= 1 jusqu nbrJoueurs t[i].join();
33
static void sleep (int millis) throws InterruptedException endort le thread courant pour millis millisecondes. void interrupt () interrompt le sommeil dun thread (en wait() ou en sleep()).
7.7
7.7.1 Introduction
Supposons que nous ayons un systme multitche, par exemple un serveur web. Le systme est compos de deux types de threads : un thread serveur, dont le code est :
tant que vrai si requete.existe() requete.executer()
et des threads clients dont le code cre des requtes. Le problme est le suivant : le code du serveur effectue ici ce que lon appelle une attente active, cest--dire quil passe son temps tester sil doit travailler. Or cette boucle consomme beaucoup de temps CPU, et ce alors mme que le thread est sens ne rien faire, ce qui est tout de mme paradoxal. La combinaison des mthodes wait et notify permet de rsoudre le problme. Pour simplier, disons que wait permet de bloquer un thread jusqu ce quun autre thread le rveille avec notify().
7.7.2 Mthodes
void wait () throws InterruptedException mthode de la classe Object. Pour appeler x.wait(), il faut se trouver dans une mthode ou un bloc synchronis sur lobjet x.
34
CHAPITRE 7. THREADS
Le thread courant se bloque, en attendant dtre reveill par un appel de notify ou notifyAll. Dautre part, le verrou quil dtenait sur x est relch, ce qui permet dautres threads synchroniss sur x de travailler.
void notify ()
throws IllegalMonitorStateException x.notify() rveille un des threads qui mis en attente par x.wait(). Le choix effectu est arbitraire. Notez que cela ne signie pas que le thread en question va forcment avoir la main tout de suite. Si aucun thread nest en attente, notify ne fait rien.
void notifyAll ()
throws IllegalMonitorStateException notify() est intressant quand les threads rveiller sont plus ou moins quivalents. Si un seul des threads en attente sur un wait() est susceptible de pouvoir travailler, rien ne garantit que notify rveillera prcisment celui-l. Dans ce cas, notifyAll peut savrer un bon choix. x.notifyAll() rveille tous les threads en attente sur x
Attention ! Ne confondez pas les threads en attente par la suite dun wait() et les threads qui essaient dobtenir un verrou pour commencer une fonction synchronized. notify() naura aucun effet sur ces derniers.
Mais ce code correspond une attente active. En fait, le code correct sera, pour le serveur :
35
client
Comment cela fonctionne-t-il? Lorsque le serveur se lance, il prend le verrou sur compteur. Puis il rentre dans la boucle. Si le compteur na pas de valeur, le serveur se met en attente et relche le verrou. Lorsquun client initialise le compteur, il effectue ensuite un notify, qui interrompt le wait. Le serveur peut nouveau tourner 2 . Lorsquil a la main, il reprend son excution, et comme estInitialis est vrai, il excute le code qui suit.
2. Mais cela ne signie pas quil reprenne la main immdiatement. Il est toujours en concurrence avec les autres threads. Pour que le serveur ait immdiatement la main, il sufrait nanmoins de lui donner une priorit suprieure celle des clients.
36
CHAPITRE 7. THREADS
requete.notifyAll(); // Prvient tout le monde que requte est modifie } // On travaille....
et le code du client :
client // On travaille ... synchronized (requete) { // On attend que la requte soit libre : while (! requete.estVide()) requete.wait(); requete.initialise(1); requete.notifyAll(); } // On travaille ...
Lorsquun client dpose une requte, il prvient le serveur et les autres clients en attente. Mais ce nest pas gnant : le serveur va pouvoir travailler, et les autres clients, comme la requte est occupe, vont se remettre en wait.
37
System.out.println("Le Thread " + id + " va empiler " + temps); serveur.empiler(temps); System.out.println("Le Thread " + id + " a empil " + temps); // yield(); } } } public class PileAttente extends Thread { /** * traiter attends val secondes, puis affiche val. */ Stack pile; public PileAttente() { pile= new Stack(); } public void traiter(int val) { System.out.println("On traite"); try { sleep(val* 1000); } catch (InterruptedException e) {}; System.out.println("Fin du traitement "+ val); } public void run() { while (true) { System.out.println("Le serveur veut lire une valeur "); int x= depiler(); System.out.println("Le serveur a lu " + x);
38
traiter(x); } }
CHAPITRE 7. THREADS
public synchronized void empiler(int v) { pile.push(new Integer(v)); notify(); // On pourrait mettre aussi notifyAll, en prvision du cas o // lon aurait plusieurs *serveurs*. Mais notifyAll serait-il // intressant ? Si plusieurs threads sont en attente pour // dpiler, ils sont quivalents. Il suffit donc den prvenir un. // do notify. } public synchronized int depiler() { int v; while (pile.empty()) { try { wait(); // On attends un notify. } catch (InterruptedException e) {}; } System.out.println("Le serveur est rveill et va lire"); v= ((Integer)pile.pop()).intValue(); return v; } public static void main(String args[]) { PileAttente s= new PileAttente(); Random r= new Random(); Client c1= new Client(s, 1, 5, r); Client c2= new Client(s, 2, 7, r); Client c3= new Client(s, 3, 10, r); s.start(); c1.start(); c2.start(); c3.start(); } }
7.8
Dmons
En premire approximation, un programme java sarrte quand tous les threads se sont arrts. Or, certains threads servent uniquement fournir des services aux autres ; leur code a gnralement la forme :
public void run() { while (true) {
7.9. GRAPHISMES
si requete traiter_requete(); sleep(1000) } }
39
Un thread avec un tel run() ne sarrterait jamais. Pour simplier la vie du programmeur, Java introduit la notion de dmon. La machine virtuelle java sarrte en fait quand les seuls threads qui tournent sont des dmons. on dclare un thread comme dmon grce la mthode setDaemon : public void setDaemon (boolean d) dclare le thread comme un dmon si d est true. Attention, ne peut tre utilise sur un thread une fois quil est lanc.
7.9
va :
Graphismes
Quand un programme graphique est lanc, java dmarre un thread, la boucle graphique, qui recevoir les vnements graphiques (clics de souris, fentre visible...) ; les traiter (en appelant les listeners correspondants) ; soccuper de lafchage. En consquence, si un programme graphique effectue un traitement long, lintgralit du programme est gel pendant ce temps : pas dafchage, ni de rponse aux actions de lutilisateur. Plusieurs solutions sont disponibles : lutilisation dun Timer swing (en particulier quand on veut une animation), le package spin (http://spin.sourceforge.net), et les threads. Lide est de placer lopration longue dans un thread. Ainsi, linterface graphique se contentera de mettre en place le thread, et de le lancer.
import java.awt.*; import java.awt.event.*; import javax.swing.*; Rebonds.java
// Observateur public class Rebonds extends JPanel { Billard billard; public Rebonds (Billard b) { billard= b; } public void paintComponent(Graphics g) { // Dessiner le fond. Obligatoire pour un bon fonctionnement !
40
CHAPITRE 7. THREADS
super.paintComponent(g); int x, y; // Erreur possible dans les deux lignes suivantes ! // Laquelle ? x= billard.getX(); y= billard.getY(); x= (x * getWidth()) / billard.WIDTH; y= (y * getHeight()) / billard.HEIGHT; g.fillOval(x - 3, y - 3, 6, 6); } public static void main(String args[]) { Billard b= new Billard(10, 31); Rebonds r= new Rebonds(b); Mouvement mouvement= new Mouvement(r, b); JFrame jf= new JFrame(); jf.getContentPane().add(r); jf.setSize(200,200); jf.setVisible(true); jf.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); }}); mouvement.setPriority(10); mouvement.start(); }
} // Modle class Billard { // Position de la balle private int x,y; public final static int WIDTH= 100; public final static int HEIGHT= 100; public Billard(int x, int y) {setPos(x,y);} synchronized public int getX() {return x;} synchronized public int getY() {return y;} synchronized public void setPos(int x, int y) {this.x= x; this.y= y;} } // Action class Mouvement extends Thread { Rebonds rebonds; Billard billard; // Direction de la balle int dx; int dy;
7.9. GRAPHISMES
41
public Mouvement(Rebonds rebonds, Billard billard) { this.rebonds= rebonds; this.billard= billard; dx= 1; dy= 2; } public void run() { while (true) { int x= billard.getX(); int y= billard.getY(); if (x + dx < 0 || x + dx > billard.HEIGHT) dx= -dx; if (y + dy < 0 || y + dy > billard.HEIGHT) dy= -dy; billard.setPos(x+dx, y+dy); // se rappeler de prvenir rebonds !!! rebonds.repaint(); yield(); try { sleep(0,01); } catch (InterruptedException e) {} } } }
Le code qui prcde montre lutilisation dun thread, en loccurrence pour lancer une animation. Mais cest valable pour tout traitement un peu long. Lerreur voque dans le code est la suivante : si le thread modie la position du point entre lexcution de x= billard.getX(); et celle de y= billard.getY();, la balle sera afche une position fausse. La solution serait de fournir une mthode getPosition synchronise.
42
CHAPITRE 7. THREADS
43
45
Les collections sont un ensemble de classes de java qui permettent dorganiser des donnes en tableaux, ensembles, dictionnaires, etc... les collections fournissent de plus une sparation entre leur implmentation effective et leur usage, permettant ainsi une meilleure rutilisabilit. Pour cela, on utilise lhritage et les interfaces. Voici tout dabord un exemple de code :
1 2 3 4 5 6 7 8 9 10 11 12
import java.util.*; public class TestCollec { public static void main(String argc[]) { Set promotion= new TreeSet(); promotion.add(new Etudiant(1, "Turing")); promotion.add(new Etudiant(2, "Babbage")); if (promotion.contains(new Etudiant(4, "Wirth"))) System.out.println("Wirth appartient la promotion"); } }
ligne 1 les collections sont dans le package java.util ligne 6 on cre une collection de classe TreeSet, et on la range dans un handle vers un Set. Nous reviendrons sur cette ligne. ligne 7 On ajoute un nouvel tudiant dans lensemble promotion , laide de la mthode add. ligne 9 On teste si Wirth appartient la promotion, grce la mthode contains. La ligne 6 est trs importante. Ce que lon veut, dans la suite du programme, cest un ensemble et en java, cela signie un objet qui implmente linterface Set . Peu importe,
46
pour la suite, la manire dont cet ensemble est implment. Par contre, lors de la cration de cet ensemble, nous sommes bien forc de choisir un implmentation effective. Nous choisissons ici TreeSet (implmentation dun ensemble grce un arbre). Un point important est que nous ne prcisons nulle part que TreeSet est un ensemble dtudiants. En fait, TreeSet est un ensemble dObject (pour ce type particulier densemble, on a de plus lobligation que les objets en question implmentent linterface Comparable, cest--dire quils possdent une mthode compareTo).
1.2
Itrateurs
Supposons que nous ayons rang des etudiants dans un tableau. Lafchage de lensemble des tudiants se ferait de la manire suivante :
for (int i=0; i< tabEtuds.length; i++) System.out.println(tabEtuds[i]);
Supposons que nous ayons cr un type liste dtudiants . Nous le parcourrions de la manire suivante :
for (ListeEtud l= listeEtuds; l != null; l= l.next) System.out.println(l.getEtud());
Du point de vue de limplmentation, ces deux approches sont trs diffrentes. Par contre, du point de vue de lutilisation, on remarque que l et i sont similaires. Ils disposent chacun des fonctionnalits suivantes : on les initialise au dbut de lensemble (i=0, l= listeEtuds) ; on dispose dun oprateur pour passer llment suivant : (i++ et l= l.next) ; on dispose dun test darrt : i < tabEtuds.length et l != null ; on dispose dune opration pour rcuprer llment courant : tabEtuds[i] et l.getEtud(). De plus, ces quatre oprations permettent conceptuellement de parcourir nimporte quel ensemble. En java, ce concept est actualis par linterface Iterator. Le code prcdent peut alors scrire, pour nimporte quelle collection, de la manire suivante :
for (Iterator i= promotion.iterator(); i.hasNext(); ) { Etudiant e= (Etudiant) i.next(); 1 System.out.println(e); }
47
Remarquez que cest promotion qui construit litrateur. Cest encore un usage de lhritage ! Les oprations vues prcdemment tant reprsentes comme suit : Cration, initialisation La cration dun itrateur est effectue par la collection elle-mme, par lappel de la mthode iterator() ; Rcupration de llment courant et avancement La mthode next() renvoie la valeur de llment courant, puis avance dun cran (cf. i++ en C). Test darrt La mthode hasNext retourne vrai sil est possible dappeler next()
1.3
Lorganisation de larbre dhritage pour les collections est le suivant : Les interfaces dnissent les grandes lignes de ce que peut faire une collection :
interface java.util.Collection interface java.util.List interface java.util.Set interface java.util.SortedSet interface java.util.Map interface java.util.SortedMap interface java.util.Map.Entry
Donnons les clefs pour comprendre ces interfaces et ces classes. Tout dabord, conceptuellement : les listes (List) sont des suites dlments. On peut, soit les parcourir du premier au dernier, soit accder au ie lment ;
1. On remarquera un problme de conception d la reprise dhabitudes provenant du C. Il serait bien plus souple et plus gnral de couper next() en deux oprations : une qui avance dans la liste, lautre qui permet de rcuprer un lment.
48
La dernire interface, Map.Entry, permet de manipuler un dictionnaire comme un ensemble (Set) de dnitions. En ce qui concerne les classes, elles fournissent des implmentations de ces interfaces. Le choix de la classe utilise dpend de lusage que lon veut en faire 2 . LinkedList fournit une liste doublement chane. Lajout dun lment est rapide, mais la recherche dune valeur donne et laccs au ie lment sont en O(n). ArrayList est une implmentation base de tableau. Lajout dun lment peut tre plus coteux que dans le cas dune LinkedList si le tableau doit grossir, mais par contre laccs au ie lment est en temps constant. Vector est un quivalent de ArrayList datant du jdk1.0. Les diffrences entre les deux se situent surtout lorsque lon utilise le multitche 3 . Stack est un type spcial de vercteur, utilis pour raliser des piles. Les piles sont des structures de donnes trs utiles, dont nous reparlerons. Pour le reste : Hash signie que limplmentation utilise un algorithme de hachage : elle associe chaque lment un nombre (par exemple, on pourrait associer une chane de caractre la somme des codes des caractres quelle contient), et elle utilise ce nombre comme indice dans un tableau. Les implmentations par hachage sont gnralement trs rapides pour des volumes moyens de donnes. Par contre, elles ne permettent pas de rcuprer les lments dans lordre. Tree signie que limplmentation utilise un arbre. Les arbres sont des structures trs robustes, adaptes de grands volumes de donnes. De plus, ils permettent de rcuprer les lments dans lordre croissant.
2. Nous ne parlerons pas ici des WeakHashMap, qui sont trop techniques pour cette premire approche 3. Pour tre complet, disons que Vector est synchronis. Pour comprendre, attendre de voir les Threads
49
1.4
Linterface Collection
// tableaux : Object[] toArray(); // Renvoie le tableau des lments de la collection Object[] toArray(Object a[]); }
Notes : add, addAll, etc... sont des mthodes de Collection qui retournent un boolen, qui prcise si la mthode a modi lensemble auquel elle sapplique. Par exemple, si X = {a, b, c} et que Y = {b, c}, X.addAll(Y) ne modiera pas X, puisque tous les lments de Y sont dans X. Donc X.addAll(Y) renverra false. Par contre, Y.addAll(X) renverrait true. Les mthodes qui doivent chercher un lment (contains, remove...) utilisent les mthodes equals des lments pour les comparer entre eux. La dernire version de toArray permet en fait de rcuprer un tableau typ. On peut crire, par exemple,
String[] x = (String[]) maCollectionDeChaines.toArray(new String[0]);
pour rcuprer dans x un tableau de String. Les itrateurs fournissent les oprations :
public interface Iterator { boolean hasNext(); // Peut-on appeler next ? Object next(); // Retourne llment courant et incrmente void remove(); // Enlve llment courant (opt) }
50
Lopration remove est optionnelle, cest--dire que certaines implmentation de linterface peuvent ne pas en donner de version utilisable. Il faut consulter la documentation.
1.5
Listes
Les listes sont des suites dlments. Lordre des lments est donc important, et est x par le programmeur. Les oprations de List sont les mmes que celles de Collection, plus :
public interface List extends Collection { // Accs direct Object get(int i);\ Rcupre le ie lment Object set(int i, Object elt); // fixe le ie elt. Opt. void add(int i, Object elt); // Ajoute elt en i e position // Dplace les suivants vers la droite Object remove(int i); // enlve le ime elt // Dplace les suivants vers la gauche abstract boolean addAll(int i, Collection c); // ajoute en pos. i les elts de c // Recherche int indexOf(Object o); // Index de la premire occurrence de lobjet o, ou -1 int lastIndexOf(Object o); // Index de la dernire occurrence de lobjet o, ou -1 // Iterateurs ListIterator listIterator(); // Itrateur de liste ListIterator listIterator(int i); // Itrateur de liste sur le i e lment // Sous listes List subList(int from, int to); // sous liste entre from inclus et // to, exclu }
Outre ces oprations, les listes fournissent des itrateurs de liste. Ceux-ci sont dots des oprations supplmentaires :
public interface ListIterator extends Iterator { boolean hasPrevious(); // Passage au prcdent possible ? Object previous(); // passage lobjet prcdent int nextIndex(); // Index suivant int previousIndex(); // Index prcdent void set(Object o); // fixe llment courant o (opt) void add(Object o); // ajoute llment o cette position }
Remarque sur les opration de recherche : contains, remove, retainAll sont spcies pour les liste en utilisant loprateur equals de llment, pas loprateur ==. Notez par contre que les oprations dajout rajoutent un lment, mme sil se trouve dj dans la liste, et
1.6. ENSEMBLES
51
que si remove(Object) supprime la premire occurrence de lobjet, c1.removeAll(c2) garantit quaprs excution, il ny a plus aucun lment contenu dans c2 qui reste dans c1.
1.6
Ensembles
Linterface densemble est la mme que celle de Collection. Il est par contre important de noter les points suivants sur les implmentations.
Cette mthode renvoie un entier ngatif, null, ou positif selon que this est (respectivement) infrieur, gal, ou suprieur o. Lautre solution est de fournir un comparateur comme argument au constructeur de lensemble. Un comparateur est un objet dune classe qui dnit la mthode suivante 4 :
int compare(Object o1, Object o2); // Compare o1 et o2 (cf. ci-dessus) // Pour des entier, o1 - o2 fait laffaire
Par exemple, ce comparateur permet de crer un ensemble dentiers tris en ordre dcroissant :
class MonComparateur implements Comparator { public int compare(Object o1, Object o2) { Integer a= (Integer)o1; Integer b= (Integer)o2; return b.intValue()-a.intValue(); } }
On lutilisera ainsi :
Set s= new TreeSet(new MonComparateur()); s.add(new Integer(5)); s.add(new Integer(7)); for (Iterator i= s.iterator(); i.hasNext(); ) System.out.println(i.next());
Ce qui imprime : 7 5 Notez lusage de Integer pour encapsuler un int dans un objet.
4. Ce nest pas lexacte vrit mais cest sufsant en pratique. Pour tout savoir, lire la doc !
52
1.7
Dictionnaires (maps)
public interface Map { // Operations de base // Range la valeur v dans la case k Object put(Object k, Object v); // Rcupre la valeur v associe k Object get(Object k); // Supprime la case de cl k Object remove(Object k); // possde-t-on un case de cl k ? boolean containsKey(Object k); // possde-t-on un case contenant la valeur v ? boolean containsValue(Object v); int size(); boolean isEmpty(); // Oprations globales // Range tous les lments de t dans this void putAll(Map t); void clear(); // Vue comme collections // Ensemble des clefs public Set keySet(); // Ensemble des valeurs public Collection values(); // Ensemble des couple clef/valeurs public Set entrySet(); // Interface pour les lments de entrySet public interface Entry { Object getKey(); Object getValue(); Object setValue(Object value); } }
Notez labsence diterator(). En fait, pour parcourir une Map, on passe par les ensembles (Set), en utilisant, lune des trois mthodes keySet(), values(), ou entrySet(), selon que lon veut parcourir lensemble des clefs, des valeurs, ou des couples clefs/valeurs. On utilise cela de la manire suivante :
Set s= dico.entrySet(); for(iterator i= s.iterator(); i.hasNext(); ) { // Map.Entry (notez lorthographe, il sagit dune sous-classe) // reprsente un couple clef/valeur dans la Map. Map.Entry e= (Map.Entry)(i.next()); System.out.println("clef " + e.getKey() + ", valeur : ", e.getValue()); }
53
1.8
Piles (Stack)
1.9
Exemples
package mesutils; import java.util.*; public class Sac extends AbstractCollection { int utilisees; // nombre de cases utilises Object tab[]; // tableau des objets de la collection // Constructeur public Sac(int capacite) { utilisees=0; tab= new Object[capacite]; } public int size() { return utilisees; } public boolean add(Object o) { tab[utilisees++]= o; return true; // On retourne vrai si la collection a chang } public Iterator iterator() { return new SacIterator(this); } // Test et dmonstration public static void main(String args[]) { Collection c= new Sac(10); c.add("I"); c.add("II"); c.add("III"); c.add("IV"); c.add("V"); c.add("VI"); c.add("VII"); c.add("VIII"); c.add("IX"); c.add("X"); for (Iterator i= c.iterator(); i.hasNext(); ) System.out.println("lment " + i.next());
54
34 35 36 37
} package mesutils; import java.util.*; public class SacIterator implements Iterator { int i; Sac s; // Seul Sac a le droit dappeler le constructeur // Celui-ci a donc la porte package. SacIterator(Sac s) { i= 0; this.s= s; } public boolean hasNext() { return i < s.size(); } public Object next() { return s.tab[i++]; } public void remove() { throw new java.lang.UnsupportedOperationException( "Remove nest pas implmente pour les sacs"); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
import java.util.*; import java.io.*; public class TestMaps { public static void main(String args[]) throws IOException { BufferedReader f= new BufferedReader(new InputStreamReader(System.in)); String s; Map dico= new TreeMap(); // Dictionnaire tri while ((s= f.readLine()) != null) { int nb= 0; if (dico.containsKey(s)) nb= ((Integer)dico.get(s)).intValue();
1.9. EXEMPLES
13 14 15 16 17 18 19 20 21 22 23 24 25 26
55
// nb contient maintenant le nombre doccurrences du mot. dico.put(s, new Integer(nb + 1)); } f.close(); // Affichage : Set entrees= dico.entrySet(); for(Iterator i= entrees.iterator(); i.hasNext();) { Map.Entry e= (Map.Entry)(i.next()); System.out.println(e.getKey() + " apparat " + e.getValue() + " fois"); } } }
56
57
Chapitre 2 Entres/sorties
INTGRER LES EXEMPLES DES TRANSPARENTS
2.1
La classe File
La classe File permet de manipuler des chiers et des rpertoires de lextrieur : elle fournit les fonctionalits ncessaires pour connatre les droits sur un chier, le dtruire, le dplacer, lister des rpertoires, etc... Par contre elle ne fournit rien pour crire dedans. Pour voir ce que permet la classe File, voici un petit programme qui dtruit les chiers de sauvegardes crs par emacs (leur nom se termine par ~ ).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
import java.io.*; // // // // //
Clean.java
FilenameFilter est une interface utilise par listFiles pour ne lister quune partie des fichiers. Chaque fichier du rpertoire est test par la mthode accept(...), qui renvoie true si le fichier doit apparatre. Ici, nous vrifions que le nom du fichier se termine par "~".
public class Clean implements FilenameFilter { public boolean accept(File dir, String name) { return (name.charAt(name.length() -1) == ~); } static public void main(String args[]) { // On cre un filtre pour listFiles. // Comme il sagit dun bte petit programme, // on utilise la classe Clean comme filtre. Clean filter= new Clean(); // dir reprsente le rpertoire nettoyer. // Son nom est pass comme argument. File dir= new File (args[0]); if (dir.isDirectory()) {
58
24 25 26 27 28 29 30 31 32
CHAPITRE 2. ENTRES/SORTIES
// On cre la liste des fichiers // dont le nom se termine par ~ File [] fichs= dir.listFiles(filter); // On efface tous ces fichiers. for (int i= 0; i< fichs.length; i++) fichs[i].delete(); } }
Nous donnons ci-aprs une slections de mthodes utiles fournies par la classe File. public File (String pathname) Cre un nouvel objet File, correspondant au chemin (path) indiqu. Par exemple, avec
File f= new File(".");
f dsignera le rpertoire courant. Le chemin peut correspondre un chier ou un rpertoire. public File (File parent, String child) Cre un nouvel objet File, correspondant au chemin (path) compos en ajoutant child parent. Ainsi, en excutant le code :
File homedir= new File("/home/rosmord"); File f= new File(homedir, "MonProg.java");
f dsignera le chier "/home/java/MonProg.java". boolean canRead () Renvoie vrai si le chier est lisible. boolean canWrite () Renvoie vrai si le chier est autoris en criture. boolean delete () Dtruit le chier ou le rpertoire correspondant. boolean exists () Renvoie vrai si le chier existe. String getName () Retourne le nom du chier. String getPath () Retourne le chemin du chier (nom compris).
59
File[] listFiles () Renvoie la liste des chiers contenus dans this, qui doit bien entendu tre un rpertoire. File[] listFiles () Renvoie la liste des chiers contenus dans this, qui doit bien entendu tre un rpertoire. File[] listFiles (FilenameFilter filter) Idem, mais ne concerne que les chiers slectionns par lter. Voir lexemple de code en introduction. boolean mkdir () Cre le rpertoire correspondant this. boolean mkdirs () this dsigne un rpertoire. Cette mthode le cre, mais cre aussi tous les rpertoires intermdiaires ncessaires, sils nexistent pas. Par exemple, avec le code :
File f= new File("/tmp/rep1/rep2/rep3"); f.mkdirs();
Si ni rep1, ni rep2, ni rep3 nexistent, les trois seront crs. boolean renameTo (File dest) Renomme un chier. boolean setReadOnly () Place un chier en lecture seule. (interdit lcriture, quoi).
60
CHAPITRE 2. ENTRES/SORTIES
2.2
2.2.1 Fichiers en lecture, chiers en criture 2.2.2 Classes orientes octets, classes orientes caractres 2.2.3 Filtrage
Les classes dentres/sorties de Java sont souvent utilises en cascade . Lide tant que le texte lu par un objet sert dentre au suivant. On remarquera que ce systme est assez similaire celui qui est utilis dans les pipe dUnix. Considrons, par exemple, la ligne :
LineNumberReader f= new LineNumberReader(new InputStreamReader(System.in));
On utilise trois objets : System.in, qui est un InputStream, cest dire un chier ouvert en lecture, et lisant des octets. InputStreamReader, qui prend en entre, le ot doctets fourni par System.in, et renvoie des caractres. LineNumberReader : lit les caractres dans lInputStreamReader, et les renvoie. Au passage, en prote pour tenir jour un numro de ligne. Lorsquune instruction comme
c= f.read();
Pour mieux comprendre le fonctionnement de ce systme, voyons une petite classe qui fourni les fonctionnalits dun Reader (Lecteur de caractres), mais compte les caractres lus :
import java.io.*; public class CompteReader extends Reader { private Reader in; private int total; // in est le Reader dans lequel nous lirons public CompteReader(Reader in) { this.in= in;
61
Quand on lit une donne dans un CompteReader, avec read(), la mthode read de CompteReader utilise celle du Reader in pour lire effectivement les caractres.
2.3
Quelques Classes
: ces
BufferedReader : cette classe utilise un tampon pour lire les donnes, cest--dire que les donnes sont lues par blocs, lesquels sont stocks en mmoire. Lavantage de cette mthode est quelle rduit normalement le nombre daccs disque. Son utilisation est transparente pour le programmeur. Un autre avantage du BufferedReader est la possibilit de revenir en arrire dans la lecture, laide des mthodes mark et reset : void reset () throws IOException remet la tte de lecture la position de la dernire marque pose.
void mark (int limite) throws IOException place une marque la position courante de lecture. largument limite permet de xer le nombre maximum de caractres qui pourront tre lus en prservant la marque. Un autre intrt du BufferedReader est la disponibilit de la mthode readLine : String readLine () throws IOException
62
CHAPITRE 2. ENTRES/SORTIES
renvoie la ligne suivante sous forme de String, ou null si on est la n du chier.
LineNumberReader : un lecteur driv de BufferedReader, mais dot dune mthode getLineNumber qui permet de connatre le numro de la ligne courante. InputStreamReader : cette classe permet de construire un Reader partir dun InputStream. Cest particulirement utile avec System.in. PushbackReader : un lecteur dot dune opration unRead() qui permet de remettre du texte dans le ot dentre, comme sil navait pas dj t lu. CharArrayReader, CharArrayWriter, ByteArrayInputStream, ByteArrayOutputStream, StringReader, StringWriter, StringBufferInputStream : ces classes permettent de lire ou dcrire dans un tableau ou dans une String. Par exemple, le code suivant lit le contenu dun chier texte et le copie dans une String :
int c; FileReader f= new FileReader(args[0]); StringWriter sw= new StringWriter(); while ((c= f.read()) != -1) { // crit c dans sw sw.write(c); } // Copie le contenu de sw dans s. String s= sw.toString(); System.out.println(s);
2.4
Exemples dutilisation
2.5. MTHODES
63
2.5
Mthodes
Nous donnons ici une selection de mthodes utiles, en nous concentrant sur les classes Reader et Writer. Pour les classes InputStream et OutputStream, les noms des mthodes sont les mmes, en remplaant char par byte .
2.5.1 Constructeurs
Les classes orientes chier Ces classes ont toute un constructeur qui prend comme argument un nom de chier, et un constructeur qui prend comme argument un objet File : FileOutputStream (File file) throws IOException FileOutputStream (String name) throws IOException FileOutputStream (String name, boolean append) throws FileNotFoundException Ouvre le chier name en criture, en le crant si ncessaire. Si append est vrai, les critures se feront la n du chier. Si le chier nexiste pas et ne peut pas tre cr, alors une exception FileNotFoundException est leve. FileInputStream (File file) throws FileNotFoundException FileInputStream (String name) throws FileNotFoundException Les classes FileReader et FileWriter ont les mmes constructeurs que FileInputStream et FileOutputStream, respectivement. Les classes OutputStreamWriter et InputStreamReader Ces deux classes permettent de faire le pont entre les classes oriente octets et les classes orientes caractres. Plus prcisment, tant donn un ot orient octets, elles permettent de crer le ot orient caractres correspondant, en encapsulant lobjet-ot dorigine dans un Reader ou un Writer.
64 Les constructeurs disponibles sont : InputStreamReader (InputStream in) OutputStreamWriter (OutputStream out)
CHAPITRE 2. ENTRES/SORTIES
classes orientes chanes Les classes StringReader et StringWriter sont trs simples : StringReader (String s) cre un StringReader qui lit dans s. StringWriter () cre un StringWriter. La chane produite sera rcuprable grce la mthode toString. Les classes de lecture qui travaillent sur des tableaux et non sur des objets sont plus complexes : CharArrayReader (char[] buf) cre un lecteur qui lit dans le tableau buf. CharArrayReader (char[] buf, int offset, int length) cre un lecteur qui lit dans le tableau buf, en commenant offset, et avec une longueur de length (soit donc entre la case offset incluse et la case offset + length exclue). Par contre les classes dcriture ont un constructeur trs simple : CharArrayWriter () Les classes ByteArrayOutputStream et ByteArrayInputStream ont les mmes constructeurs que leurs quivalent en Writer et en Reader, en remplaant les chars par des bytes. Autres classes Les autres classes fonctionnent gnralement comme des ltres. Elles disposent donc typiquement dun constructeur qui prend comme argument un objet de la mme famille que lobjet crer (Writer pour Writer, Reader pour Reader, InputStream pour InputStream et OutputStream pour OutputStream). Ainsi, le constructeur de la classe LineNumberReader est : LineNumberReader (Reader in)
2.5. MTHODES
65
2.5.2 Reader
int read () throws IOException lit un caractre sur lentre, et renvoie son code, ou -1 si la n du chier est atteinte.
int read (char [] buf) throws IOException Remplit le tableau buf avec les caractres lus. Attention, cette mthode nalloue pas le tableau. La mthode renvoie le nombre de caractres lus, ou -1 si la n du chier est atteinte. int read (char [] buf, int dep, int longueur) throws IOException Copie les caractres lus dans buf, en commenant lindice dep, en en lisant au maximum longueur caractres. La mthode renvoie le nombre de caractres lus, ou -1 si la n du chier est atteinte. void close () ferme le lecteur. throws IOException
2.5.3 Writer
Mthodes gnrales void write (int c) throws IOException crit le caractre dont le code est c. Notez que cette mthode fonctionne correctement que c soit un char ou un int.
void write (char [] s) throws IOException crit le contenu de s. void write (char[] s, int off, int longueur) throws IOException crit les caractres de s compris entre lindice off et off + longueur. void write (String s) throws IOException crit la chane s.
66 void flush ()
CHAPITRE 2. ENTRES/SORTIES
throws IOException Ralise effectivement la copie sur le support. Cest utile dans le cas de classes comme BufferedWriter, o les donnes sont stockes temporairement en mmoire.
throws IOException
La classe FileWriter permet dcrire du texte dans un chier. Son emploi est simple. On notera les deux contructeurs suivants : FileWriter (String name) throws IOException Ouvre en criture le chier de nom name. FileWriter (String name, boolean append) throws IOException Ouvre en criture le chier de nom name. Si append est true, alors lcriture se fera la n du chier, sans craser le texte qui se trouve ventuellement au dbut.
2.6
La classe RandomAccessFile
Cette classe permet douvrir un chier dans lequel on pourra, lire, crire, et se placer nimporte quelle position.
throws IOException
67
On dispose, de plus, des mthodes readBoolean, readByte, readChar, readDouble, readFloat, readInt, readLong, readShort, readUnsignedByte, readUnsignedShort pour lire une valeur dun type de base. Par exemple : boolean readBoolean () throws IOException lit un boolen. Attention, readByte lit un octet sign (entre -127 et 128.) Les mthodes dcriture sont similaires : on a dune part trois mthodes write : void write (byte []b, int dep, int longueur) throws IOException crit les octets du tableau b, de lindice dep lindice dep + 1. et dautre part, une mthode writeXXX par type de base, comme par exemple writeChar. Notez que Java dnit lordre dans lequel sont crit les octets des donnes lues, de manire indpendantes de larchitecture. Ainsi, un chier binaire cr par java sur Macintosh sera utilisable aussi sur PC
2.6.3 Dplacement
long getFilePointer () throws IOException retourne la position courante. void seek (long pos) throws IOException xe la position courante. pos est le nombre de caractres depuis le dbut du chier. La prochaine lecture ou la prochaine criture auront lieu partir de la nouvelle position. throws IOException retourne la longueur de ce chier
long length ()
68
CHAPITRE 2. ENTRES/SORTIES
2.7
Srialisation
Java permet dcrire ou de lire directement des objets dans un chier. On utiliser ce systme pour sauvegarder un objet complexe, presque sans effort. On peut, par exemple, crire :
TreeSet s; ... // Code qui remplit s ... ObjectOutputStream o= new ObjectOutputStream(New FileOutputStream("toto.sav")); o.writeObject(s); o.close();
Et le contenu de notre TreeSet sera sauv dans le chier toto.sav . Pour relire les donne, cest peine plus compliqu :
ObjectInputStream f= new ObjectInputStream(New FileInputStream("toto.sav")); TreeSet s= (TreeSet)f.readObject(); f.close();
2.8
La classe StreamTokenizer
2.8.1 Introduction
La classe StreamTokenizer permet de lire de manire souple un chier texte, en le dcoupant en mots. Ces mots sont appels token. Le Tokenizer fonctionne en deux tapes : tout dabord, une mthode (nextToken()) permet de lire le mot suivant. Ensuite, il est possible dinterroger
69
le Tokenizer sur la valeur de ce mot, et ventuellement sur son type (par exemple, si cest une chane de caractre, une ponctuation, ou un nombre). Lexemple qui suit prsente une utilisation possible de StreamTokenizer. Il sagit dun petit programme qui lit sur lentre standard. Si on tape des nombres, ceux-ci sont ajout un total. En tapant print , on obtient lafchage de ce total. La difcult pour raliser ce genre de programme est quon ne sait pas a priori ce quon va lire : print ou un nombre. Le programme en lui mme est simple, mais, sans StreamTokenizer, sa programmation serait assez dlicate. Lavantage avec StreamTokenizer est quon lit le token suivant sans se demander ce que cest, et quon prend une dcision aprs sa lecture, ce qui facilite la programmation.
Dmonstration de StreamTokenizer import java.io.*; public class TestTok { public static void addition() throws IOException { double total= 0.0; int token; // Cration dun StreamTokenizer sur lentre standard StreamTokenizer s= new StreamTokenizer(new InputStreamReader(System.in)); // On lit lentre jusqu ce que le texte soit fini while( (token= (s.nextToken())) != StreamTokenizer.TT_EOF) { //Dans token on a le type du mot lu : mot, nombre, ou autre switch (token) { case StreamTokenizer.TT_NUMBER: // Si cest un nombre, sa valeur est dans nval : total+= s.nval; break; case StreamTokenizer.TT_WORD: // Si cest un mot, elle est dans sval if (s.sval.equals("print")) System.out.println("total= " + total); else System.out.println("le mot clef " + s.sval + " est inconnu"); break; // Pour le reste des caractres, le token // a comme valeur le code du caractre default: System.out.println("chane inattendue: " + (new Character((char)token))); } } } public static void main(String args[]) addition(); throws IOException {
70
} }
CHAPITRE 2. ENTRES/SORTIES
2.8.2 Documentation
Constructeurs Il existe deux constructeurs pour les StreamTokenizer, mais lun dentre eux est obsolte. Nemployez donc que le suivant : StreamTokenizer (Reader r) Construit un StreamTokenizer qui lit sur lentre r. Mthodes Les mthodes peuvent tre groupes en deux ensembles : celles qui permettent de congurer le StreamTokenizer, et celles qui sont utilises lors de la lecture du texte. Certaines mthodes de conguration permettent de xer le comportement global dun StreamTokenizer. Par exemple, parseNumbers lui demande danalyser les nombres comme des nombres et non comme des suites de caractres. Dautres mthodes permettent de grer le comportement dun caractre en particulier. void eolIsSignificant (boolean flag) Si ag est true, alors la n de ligne est trait comme un caractre ordinaire . Sinon, on la considre comme un espace. Par dfaut, cest cette seconde option qui est prise. void lowerCaseMode (boolean flag) Si ag est vrai, alors le texte est automatiquement traduit en minuscule, ce qui est utile pour les langages qui ignorent les diffrences majuscules/minuscules (ou diffrences de casse en langage typographique).
71
void parseNumbers () Si cette mthode est appele, les nombres seront considrs comme tels. Le StreamTokenizer fourni par dfaut analyse les nombres (do une lgitime interrogation : pourquoi diantre les concepteurs ont-ils fourni cette mthode, qui ne sert rien, et pas de mthode noParseNumbers, qui aurait servi quelque chose. void slashSlashComments (boolean flag) Si ag est vrai, // introduit un commentaire. Le texte qui suit est ignor jusqu la n de la ligne. void slashStarComments (boolean flag) Si ag est vrai, les commentaire de type C sont utiliss. Le texte compris entre /* et */ est ignor. void resetSyntax () Remet le StreamTokenizer zro. Tous les caractres sont alors considrs comme ordinaires . Un caractre ordinaire est trait tout seul, comme par exemple un signe de ponctuation. Les mthodes qui suivent permettent de spcier comment traiter des caractres donns. Par exemple, si lon crit
tok.commentChar(#);
le caractre # introduira une ligne de commentaires (cest dire que la ligne en question sera saute par le StreamTokenizer). void commentChar (int ch) Le caractre ch introduit des lignes de commentaires. void ordinaryChar (int ch) Le caractre ch est considr comme un caractre ordinaire . void ordinaryChars (int low, int hi) Tous les caractres dont les codes sont compris entre low et hi sont considrs comme ordinaires. void quoteChar (int ch) Le caractre ch est utilis comme guillemets. void whitespaceChars (int low, int hi) Tous les caractres dont les codes sont compris entre low et hi sont considrs comme des espaces. void wordChars (int low, int hi) Tous les caractres dont les codes sont compris entre low et hi sont considrs comme des lettres, qui forment des mots.
72
CHAPITRE 2. ENTRES/SORTIES
Mthodes permettant dutiliser le StreamTokenizer : La mthode de loin la plus importante est bien entendu nextToken. On notera le comportement intressant de toString. int lineno () Renvoie le numro de la ligne courante. int nextToken () Lit le token suivant, et renvoie son code. Les espaces et les commentaires sont ignors de manire transparente. Les codes possibles sont : TT_EOF n de chier ; TT_EOL n de ligne (si s.eolIsSignificant(true) a t appel ; TT_WORD un mot a t lu. Un mot est une suite de lettre, cest--dire de caractres dclars comme wordChars. Par dfaut, les lettres sont considres ainsi. TT_NUMBER un nombre (rel) a t lu. autre code : tous autre code signie quun caractre ordinaire (un signe de ponctuation par exemple) a t lu. Le code renvoy est dans ce cas le code du signe lui-mme. Noubliez pas que ces constantes doivent tre prcdes du nom de la classe StreamTokenizer. void pushBack () Remet le dernier token lu sur le ot de lecture. Le prochain nextToken() le relira. Cette mthode nest pas trs souvent utile. String toString () Renvoie le token courant sous forme de chane. Champs Les champs utilisables de StreamTokenizer sont de deux types : des constantes, comme TT_NUMBER ou TT_EOF, et des champs variables, comme nval et sval. Il aurait sans dout t prfrable que ces derniers fussent remplacs par des mthodes. Les constantes sont dcrites dans la documentation de nextToken(), ci-dessus. Les autres champs sont : nval si le dernier token est de type TT_NUMBER, sa valeur numrique. sval si le dernier token est de type TT_WORD, sa valeur sous forme de chane de caractres. Notez que si lon veut la valeur dun token quelconque sous forme de chane, on peut utiliser toString(). ttype type du token courant. Cest la valeur renvoye par le dernier nextToken().
73
Dans le cas o on ne veut pas que les nombres soient analyss comme des nombres, il faut rednir la syntaxe de tous les caractres. Le code suivant convient :
StreamTokenizer tok= new StreamTokenizer(....); tok.resetSyntax(); tok.wordChars(a, z); tok.wordChars(A, Z); tok.wordChars(128 + 32, 255); tok.whitespaceChars(0, );
Notez enn que le StreamTokenizer est assez limit, et surtout orient vers lanalyse de code informatique. En particulier, tous les caractres dont les codes sont suprieurs ou gaux 256 sont traits comme des lettres. Pour du texte non informatique, les problmes sont bien plus complexes ; les bibliothques java fournissent le package java.text.
74
CHAPITRE 2. ENTRES/SORTIES
75
3.1
Introduction JDBC
1. Ncessit dutiliser un langage appropri pour interroger une base de donne (SQL) ; 2. Ncessit dun systme client serveur cause des restrictions des applets.
3.2
Architecture
Application java Applet Java
JDBC
3.3
Un exemple : postgres
les sources, dans
76
Pour spcier le driver JDBC utiliser, deux mthodes : 1. compiler les sources avec la ligne :
% java -Djdbc.drivers=org.postgresql.Driver monfic.java
Attention ! Le driver doit se trouver dans le CLASSPATH ; sil sagit dun chier jar, le chier lui mme doit tre dans le classpath :
export CLASSPATH=.:/home/titi/postgresql.jar:/usr/local/jdk1.2
3.4
tablir la connexion
On utilise :
Connection DriverManager.getConnection(String url, String login, String passwd);
3.4.1 Exemple :
try { Connection db; String url= "jdbc:postgresql://localhost/guest"; db= DriverManager.getConnection(url, "guest", "toto"); // manipulations diverses : .... // on a fini : db.close(); } catch (SQLException e) { }
ladresse est ici compose du nom du serveur postgres (localhost) suivi du nom de la base de donne (ici, guest ; lIUT ce sera votre nom de login).
77
3.5
les requtes sont reprsentes par la classe Statement ; les modications de la base sont Statement.executeUpdate(String) ;
les requtes sont effectues par la mthode Statement.executeQuery(String) ; Le rsultat dune requte Query est un ResultSet
// On cre un canal de communication Statement st= db.createStatement(); // On envoie une requte ResultSet res= st.executeQuery("select * from Etud"); // Tant quil y a des lignes dans le rsultat.. while (res.next()) { // on lit les valeurs des champs System.out.println("col 1 = " + rs.getString("Nom")); } res.close(); st.close();
Notes : on peut avoir plusieurs requtes ouvertes sur la mme connexion ; il nest possible daccder un champ quune fois et une seule ; il est ncessaire de fermer (close) les Statement et les ResultSet.
3.5.1 Mthodes
ResultSet executeQuery (String requete) throws SQLException Envoie une requte SQL, (normalement de type select ), et renvoie le rsultat sous forme dun ResultSet. Le rsultat nest jamais null. int executeUpdate (String requete) throws SQLException Excute une requte de modication des donnes ou de la base (bref, tout ce qui nest pas select). La valeur retourne normalement le nombre de lignes modies, ce qui a un sens pour insert, delete, update. Pour les autres oprateurs, le rsultat est 0.
78
boolean execute (String requete) throws SQLException Envoie une requte SQL qui peut mme envoyer plusieurs rsultats. Le rsultat est true si la premire valeur renvoye est un ResultSet. Nous dtaillons plus avant la mthode execute en 3.5.3.
3.5.3 Execute
La mthode execute() permet denvoyer une requte, quelle soit de type select ou quelle soit une modication dune base. Les mthodes utilises dans lexemple suivant permettent de rcuprer des informations sur la requte. Bien entendu, dans la plupart des cas, le programmeur sait quelle est la requte, et donc utilise executeQuery ou executeUpdate(). La mthode execute() sera, par exemple, utilise dans un programme o lutilisateur pourra saisir une requte SQL quelconque.
Utilisation gnrale de Execute stmt.execute(queryStringWithUnknownResults); while(true) { int rowCount = stmt.getUpdateCount(); if(rowCount > 0) { // Des donnes ont t modifies System.out.println("Rows changed = " + count); stmt.getMoreResults(); continue; } if(rowCount == 0) { // Modification de la Structure, // ou pas de changement. System.out.println(" Pas de ligne modifie, ou la ligne est une commande DDL"); stmt.getMoreResults(); continue; } // Si on arrive ici, il sagit dune requte ResultSet rs = stmt.getResultSet; if(rs != null) { ... // Il faut utiliser les mtadata pour connatre // la liste des colonnes while(rs.next())
79
3.6
Commandes prpares
classe PreparedStatement ; typiquement, commande utilise plusieurs fois en changeant la valeur de certains paramtres ; les paramtres qui changent sont remplacs dans la commande par des ? ; les commandes setXXX (o XXX est le type de la variable) permettent de spcier la valeur des paramtres.
PreparedStatement pstmt = connec.prepareStatement("UPDATE table4 SET m = ? WHERE x = ?"); ... pstmt.setString(1, "Hi"); for (int i = 0; i < 10; i++) { pstmt.setInt(2, i); int rowCount = pstmt.executeUpdate(); }
NULL : pour que la valeur dun paramtre soit NULL , il suft dutiliser la commande setNull
3.7
But :
chappements SQL
avoir une plus grande portabilit, et faciliter la cration de commandes SQL. Dans la chane de commande SQL :
Syntaxe :
{commande arguments}
80
3.8
par dfaut, chaque requte forme une transaction ; pour changer ce comportement, on manipule lobjet Connection li la base de donne:
maconnexion.setAutoCommit(false);
ensuite : maconnexion.commit(); valide les requtes dj effectues lors de cette transaction ; maconnexion.rollback(); annule les requtes dj effectues ;
o level peut valoir : TRANSACTION_READ_UNCOMMITTED TRANSACTION_READ_COMMITTED TRANSACTION_REPEATABLE_READ TRANSACTION_SERIALIZABLE TRANSACTION_READ_UNCOMMITTED on peut lire des modications ds quelles sont faites. En cas de ROLLBACK, postrieur, les valeurs lues peuvent tre fausses ; TRANSACTION_READ_COMMITTED on ne peut pas lire une range sur laquelle il y a des modications non valides (par commit) ;
81
TRANSACTION_REPEATABLE_READ idem ; de plus, vite le cas o la transaction lit une range, une autre transaction la modie, et la premire relit la range modie ; la lecture donne toujours le mme rsultat, do le nom ; TRANSACTION_SERIALIZABLE le comportement est similaire celui obtenu avec un traitement squentiel. Empche le cas o 1. la transaction fait un select avec une condition ; 2. une seconde transaction cre des lignes qui satisfont la condition ; 3. la premire transaction refait le mme select.
3.9
Se rcupre grce la mthode getMetaData() de Connection les mthodes permettent de connatre les capacits de la base. Par exemple : supportsSelectForUpdate() renvoie vrai si la base permet dutiliser un select dans un update (cf. le cours de SQL !) des mthodes permettent dobtenir le catalogue de la base et la liste des tables : ResultSet getTables (String catalog, String schemaPattern, String tableNamePattern, String[] types) throws SQLException revoie un ResultSet dcrivant les tables et les index de la base. Par exemple, pour afcher la liste des tables et index :
rs= meta.getTables(null , null, null, null); while (rs.next()) { System.out.println(rs.getString("TABLE_NAME"); } rs.close();
Les arguments de getTables peuvent tre nuls. Les plus intressants sont : types : un tableau de chanes de caractre, donnant le type des tables rcuprer, entre autres : TABLE pour les tables stricto sensu, VIEW pour les vues.
3.10
82
3.11
Extensions du jdbc2.0
83
Un ResultSet fonctionne comme un tableau dont les cases sont numrotes de 1 n. Il dispose de plus de deux positions spciales, beforeFirst et afterLast, aux deux extrmites du tableau. Le curseur est lorigine plac sur beforeFirst. void last () throws SQLException se place au dernier enregistrement.
void first ()
throws SQLException se place au premier enregistrement. throws SQLException se place aprs le dernier enregistrement.
void afterLast ()
void beforeFirst ()
void next ()
throws SQLException avance lenregistrement suivant. throws SQLException avance lenregistrement prcdent.
void previous ()
boolean absolute (int i) throws SQLException se place sur lenregistrement numro i. Si i vaut 1, cest lquivalent de first. Si i est ngatif, on numrote partir du dernier enregistrement. La fonction renvoie true si le curseur pointe sur un enregistrement valide. boolean relative (int delta) throws SQLException Dplacement relatif la position courante. relative(-1) est quivalent previous, et relative(1) next(). La fonction renvoie true si le curseur pointe sur un enregistrement valide. int getRow () throws SQLException renvoie lindice de la ligne courante.
Un ResultSet nest modiable que si on la demand et que le driver le gre. Les mthodes principales sont (remplacer XXX par int, String...) : void updateXXX (int i, XXX a) throws SQLException e champ, de type XXX, en lui donnant la valeur a. modie le i void updateXXX (String name, XXX a) throws SQLException modie le champ nomm name, de type XXX, en lui donnant la valeur a. void deleteRow () throws SQLException dtruit la ligne courante.
void moveToInsertRow () throws SQLException se place sur une ligne spciale, qui sert aux insertions de nouvelles donnes. void moveToCurrentRow () throws SQLException aprs un appel moveToInsertRow, revient sa position initiale. void insertRow () throws SQLException insre le contenu de la ligne dinsertion dans la base.
85
4.1
Rappel sur notions de client et serveur ; javascript et les applets java sexcutent sur le client ; au contraire, les applications cgi, les scripts php, les jsp et les servlets sexcutent sur le serveur. Exemple dun script php : le client demande une page, qui est en fait un script php. Le serveur excute le script, dont le rsultat (typiquement une page html) est expdi comme rponse, puis visualis par le client.
4.2
4.2.1 principes
Le principe dune jsp est similaire celui dun script php. Il sagit de texte html mlang du code, crit en java dans le cas des jsp.
86
Une page jsp est traite par le serveur (tomcat par exemple), qui cre le code dune classe java associe. Par exemple, pour une page jsp nomme test.jsp, tomcat cre une classe test$jsp.java. chaque page jsp correspond donc une classe java. Le code html et une partie du code jsp proprement dit sont utiliss par le serveur pour produire la mthode _jspService, qui est appele lexcution de la jsp pour crer la page rsultat.
<%= %> : cette balise encadre une expression java. La valeur de cette expression sera afche dans la page rsultat :
<html><body> bonjour, voici la valeur de 3 + 4 : <%= 3 + 4 %> </body></html>
<%! %> : cette balise permet dajouter des mthodes et des champs la classe qui correspond la jsp. <%@ page ... %> : la balise directive de page sert congurer un certain nombre doptions pour la jsp. Parmis celles-ci, citons : <%@ page import="..." %> permet de raliser dimporter des packages java, comme par exemple :
<%@ page import="java.sql.*" %>
<%@ page include="toto.jsp" %> : inclut le chier toto.jsp. <%@ page errorPage="toto.jsp" %> : en cas de leve dexception sur cette page, on afchera toto.jsp. La variable exception contiendra lobjet exception correspondant. <%@ page isErrorPage="true" %> : signie que cette page est une page de traitement dexception (voir directive prcdente).
87
4.3
88
jsp et de servlets. Si vous regardez le contenu de test1, vous constaterez que rien nindique a priori quil contient une servlet :
bash-2.05a$ pwd /home/rosmord/Prog/testTomCat/webapps bash-2.05a$ ls -R test1 test1: index.html WEB-INF test1/WEB-INF: classes web.xml test1/WEB-INF/classes: TestServlet.class bash-2.05a$
Cependant, il en existe bien une, accessible ladresse : http://localhost:9180/test1/salut. En fait, les servlets sont contenues dans le rpertoire WEB-INF. Ce rpertoire contient : un chier web.xml, qui permet de congurer les servlets et en particulier de les associer une URL ; un rpertoire classes, qui contient des classes java (et ventuellement des packages) ; un rpertoire lib, qui contient des archives jar (par exemple, les bibliothques ncessaires pour jdbc). Quand une servlet est crite, il faut la compiler, et placer le rsultat dans le rpertoire classes. Les commandes suivantes peuvent convenir :
export CLASSPATH CLASSPATH=/usr/share/java/servlet-2.3.jar javac Hello.java mv Hello.class testTomCat/webapps/appli1/WEB-INF/classes/
Il reste cependant prciser comment on lance la servlet. Cest le travail du chier web.xml. Pour chaque servlet, il contient deux informations : la classe associe la servlet ; lurl associe la servlet. Ainsi, le chier suivant permet de prciser que la servlet bonjour est associe lurl hello et la classe TestServlet . vitez dutiliser des accents ou autres caractres non ascii 1 .
<?xml version="1.0" ?> <!DOCTYPE web-app web.xml
89
> <!-- les formules magiques ci-dessus sont a reprendre telles quelles :-) --> <!-- Le corps du fichier est compris dans la balise web-app : --> <web-app> <!-- la balise servlet lie la servlet a la classe qui limplemente. --> <servlet> <servlet-name>bonjour</servlet-name> <servlet-class>monPackage.TestServlet</servlet-class> </servlet> <!-- lie la servlet a une url --> <servlet-mapping> <servlet-name>bonjour</servlet-name> <url-pattern>/salut</url-pattern> </servlet-mapping> </web-app>
90
import javax.servlet.http.*;
public class TestServlet extends HttpServlet { int valeur; public void init() throws ServletException { super.init(); valeur=0; } public void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException { valeur++; // On prcise que le rsultat est une page de texte simple : // (pour du html, ce serait : text/html) httpServletResponse.setContentType("text/plain"); // out permettra dcrire ce rsultat : Writer out= httpServletResponse.getWriter(); // On cre la page. out.write("valeur du compteur : "+ valeur); } }
La dernire mthode importante est destroy, qui est appele lors de la destruction de la servlet. Attention ! une servlet peut tre dtruite quand le serveur le juge opportun. Il ne faut donc pas compter sur la conservation des donnes en mmoire entre deux appels de servlets.
4.4
Les javabeans
Les protocoles web classiques ne grent pas de session. Typiquement, un utilisateur demande une page web au serveur, et celui-ci la renvoie. Il nest pas prvu initialement de conserver sur le serveur des informations sur lutilisateur, comme un mot de passe utilis pour interroger une base de donnes, ou une corbeille (liste de produits commands. Il existe plusieurs mthodes pour contourner ces difcults. Larchitecture JSP/Servlets propose une solution simple et lgante, les javabeans. Un javabean est un objet java, identi par un nom (une chane de caractres), dont la dure de vie peut dpasser la simple requte http. Les beans sont utilisables partir des jsp
91
avoir des accesseurs (imprativement nomms setXX et getXX, o XX est le nom dun champ. la classe doit tre explicitement situe dans un package. La classe suivante prsente un bean trs simple :
package test; Le bean Compteur
public class Compteur implements java.io.Serializable { private int valeur; public Compteur(int v) { valeur= v; } public Compteur() { valeur= 0; } public void incremente() { valeur++; } public int getValeur() { return this.valeur; } public void setValeur(int argValeur) { this.valeur = argValeur; } }
92
Les balises spciques au beans sont : <jsp:useBean ... /> permet de dclarer un bean et de lui donner un nom et une dure de vie. Lobjet correspondant est cr sil nexiste pas, et rutilis sil existe dj. Les attributs remplir sont : id : Le nom donn au bean. Ce nom permet de lidentier et de le rcuprer. Par ailleurs, le bean est accessible dans le code java de la jsp travers une variable qui a le nom indiqu. Do le code
<% cpt.incremente(); %>
Dans notre exemple. scope : dure de vie du bean. Peut tre xe page, request, session ou application. En pratique, les deux derniers sont les plus intressants. Loption session dsigne une session de travail de lutilisateur. La session commence la connexion, et se termine quand lutilisateur arrte son navigateur, ou aprs un temps dinactivit paramtrable (xable par la mthode setMaxInactiveInterval(int interval) de la classe HttpSession ; cette mthode prend comme argument une dure en secondes). Chaque utilisateur connect aura sa propre version dun bean session. Loption application signie que le bean existe pour toute la dure de vie du serveur tomcat ; le bean est alors unique pour tous les utilisateurs. class : la classe utilise pour implmenter le bean. Le nom de la classe doit tre de la forme nompackage.NomClasse. <jsp:getProperty ... /> cette balise sera remplace dans la page HTML rsultat par la valeur dun des attributs du bean. Les attributs remplir sont : name : nom du bean (celui dni prcdemment par id="..." ;
93
property : nom de la proprit, qui doit tre un des champs dnis par le bean. Pour que cela fonctionne, la classe bean doit disposer daccesseurs correspondant au mme nom. <jsp:setProperty ... /> permet de xer la valeur dun champ du bean. Les attributs sont : name : nom du bean ; property : nom du champ ; value : valeur donner au champ.
On peut ensuite rcuprer ou donner une valeur dun bean en utilisant : les mthodes getValue et putValue dans la version 3 de tomcat ; les mthodes getAttribute et setAttribute dans la version 4. Seul le nom des mthodes change. Leur utilisation est la mme. void setAttribute (String nomBean, Object bean) cre un nouveau bean :
session.setAttribute("cpt", new Compteur());
Object getAttribute (String nomBean) rcupre lobjet bean associ un nom. Par exemple :
Compteur c= (Compteur)session.getAttribute("cpt"); c.incremente(); // ...
application La mthode getServletContext() de la classe GenericServlet (dont hrite toute servlet) renvoie un objet de classe ServletContext, qui reprsente lapplication. Cet objet dispose lui aussi de mthodes setAttribute et getAttribute.
94
4.5
Pour avoir le code le plus propre possible, on utilise servlets et jsp uniquement dans la couche interface utilisateur. Pour le reste de lapplication, on utilise des classes java normales, typiquement spares en plusieurs couches : logique applicative, modle, persistence. De plus, on utilise les jsp uniquement pour les afchages 2 . Le traitement des requtes est quant lui con des servlets.
Il existe aussi une mthode sendRedirect, demploi plus simple. Cependant, cette dernire est beaucoup moins efcace, car elle demande au client de procder une redirection. Gnralement, la servlet a trait la requte, et a modi ou cr des donnes. La jsp vers laquelle on redirige le traitement aura souvent pour fonction dafcher le rsultat de ces traitements. Ces donnes doivent donc tre passes la jsp. On pourrait le faire par lintermdiaire de javabeans, mais pour viter de charger le serveur, la meilleure manire est de dajouter ces donne dans le champ request, en utilisant la mthode setAttribute de la classe ServletRequest : void setAttribute (java.lang.String name, java.lang.Object o) Stocke o sous le nom name dans la requte. Si o est null, alors lattribut est supprim dans la requte.
String url= "adresse de la jsp afficher"; RequestDispatcher dispatcher= getServletContext().getRequestDispatcher(url); dispatcher.forward(request, response);
4.6
La gestion des sessions seffectue en utilisant souvent les cookies. Cependant, les utilisateurs ne les acceptent pas toujours. Il existe une autre manire pour grer les numros de session, cest
2. Notez que rien ninterdit de coner lafchage des servlets. Cependant, les spcialistes de design web ntant gnralement pas des programmeurs, on prfrera les jsp pour la prsentation.
95
de le passer comme paramtre dune requte lautre, en le codant dans les url. Il est possible de faire automatiquement grer les deux modes par le serveur. Pour cela, les url doivent tre spcies en utilisant la mthode encodeURL de la classe HttpServletResponse.
<begin> Page <a href="<%= response.encodeURL("next.jsp") %>">suivante</a>. <end>
Quand lutilisateur accepte les cookies, le numro de session sera stock comme un cookie, et lURL sera next.jsp . Quand il les refuse, le numro de session sera inclus dans lURL :
URL gnre par encodeURL() next.jsp;jsessionid=FAC06A1658A6C485294302CD336BA48F
96
4.7
Pour simplier la vie des programmeurs de page web, et leur cacher autant que possible la couche java, il est possible de crer de nouvelles balises. Le code excut par ces balises sera dni dans des classes java. De telles bibliothqyes de balises (taglibs) sont disponibles sur le web. Voir par exemple : http://jakarta.apache.org/taglibs. On cre pour cela une bibliothque de tags, dont l
4.7.1 Une balise simple 4.7.2 Une boucle 4.7.3 Partage de variables
4.8
Quelques patterns
97
5.1.1 Introduction
pourquoi XML : format gnrique de textes structurs. Les chiers complexes en format texte (exemple : sources povray, java...) ne sont pas facile analyser. Il faut gnralement dcrire le format laide dune grammaire, puis crire un programme en consquence. Cela demande une certaine exprience. XML est (relativement) simple ; de plus le mme code peut analyser nimporte quel document XML. historique de xml : sgml, html. XML descend de SGML (structured generic markup language), utilis au dpart pour crire des documentations techniques. domaines demplois de xml. 1. format de sauvegarde et dchange portable. Les dernires versions de word utilisent XML, de mme quOpenoffice. Noter que les documents Openoffice sont sauvegards sous la forme dun chier zipp contenant plusieurs documents XML. ; 2. format de documents smantiques. Les documents XML sont lisibles et manipulables par les navigateurs web rcents. 3. base de donnes XML.
le corps du document est plac entre une balise ouvrante et une balise fermante ; les balises (tags) sont toujours groupes par deux : une balise ouvrante (<Nom>) et la balise fermante (</Nom>) correspondante. ces balises sont correctement parenthses. On ne doit pas les croiser. Par exemple : <A><B>du texte</A></B> est incorrect. On devrait avoir <A><B>du texte</B></A>. Lorsque le contenu dune balise est toujours vide, la balise ouvrante est aussi fermante. En xml, le tag IMG de html se coderait :
<img src="...."/>
les valeurs des attributs sont toujours indiques entre guillemets. Quand un document est cod dans un code diffrent de lUTF-8, il doit le prciser dans son en-tte. Attention, cette rgle est vraie mme si les caractres non UTF-8 napparaissent que dans des commentaires. Ainsi,
<?xml version="1.0" encoding="iso-8859-1"?>
dclare que le document est cod en iso-8859-1, le codage standard sous Unix.
Exemple de document XML <?xml version="1.0" encoding="iso-8859-1"?> <promotion id="premire anne info"> <etudiant id="1"> <nom>Turing</nom> <prenom>Alan</prenom> <passe/> </etudiant> <etudiant id="2"> <nom>Lovelace</nom> <prenom>Ada</prenom> <passe/> </etudiant> </promotion>
Document Type Denition (DTD) Une DTD dcrit la structure que peut avoir un type de document XML. Il existe de nombreuses dtd dj faites, et il est possible (et relativement ais) den crer de nouvelles. Au lieu de rinventer la poudre, on aura cependant intrt rutiliser tout ou partie de DTD existantes.
99
La DTD suivante dcrit un format (trs simpli) utilisable pour un livre. Celui-ci est constitu dune suite non vide de chapitre, chacun de ceux-ci tant form dun titre, dun contenu, et dun commentaire optionnel.
<!ELEMENT <!ELEMENT <!ELEMENT <!ELEMENT <!ELEMENT <!ATTLIST livre (chapitre)+> chapitre titre contenu commentaire commentaire id type auteur livre.dtd
ID (accord|desaccord) CDATA
Une DTD dcrit, entre autres, les lments qui composent un document (les balises), les attributs que ceux-ci peuvent avoir, ainsi quun certain nombre de Dclaration des lments La dclaration des lments est la partie la plus complexe de la DTD. Elle a la forme :
<!ELEMENT nomElement (DescriptionElement)>
o : nomElement est le nom de llment (attention, XML est sensible la casse du texte) ; descriptionElement explique ce que peut contenir llment en utilisant un langage spcial. Pour dcrire les lments de ce langage, prenons comme convention que tout groupe en caractres obliques correspond une construction de la liste.
nomElement : un autre lment ; A,B : la squence A, B puis C ; A |B : A ou B. ; A * : A rpt 0 ou plusieurs fois ; A + : A rpt 1 ou plusieurs fois ; A ? : optionellement A ;
#PCDATA : du texte analys. Ainsi, la dclaration :
<!ELEMENT section (titre, auteur?, (paragraphe|image)*)>
100
indique quun lment section est compos dun lment titre, suivi optionellement dun lment auteur, suivi dune suite ventuellement vide de paragraphes et/ou dimages. Quand un lment peut contenir directement du texte, il y a quelques contraintes : #PCDATA doit apparatre en premier dans la description de son contenu ; si le #PCDATA nest pas seul, la description a forcment la forme :
<!ELEMENT nomElement (#PCDATA|nomElt1|nomElt2|...)*>
Dans certains cas, un lment peut ne pas avoir de contenu. On le dclare alors :
<!ELEMENT nomElement EMPTY>
Par exemple :
<!ELEMENT image EMPTY>
Dclaration des attributs Les attributs dun lment sont dclars de la manire suivante :
<!ATTLIST NomElement NomAttribut1 TypeAttribut1 DefautAttribut1 NomAttribut2 TypeAttribut2 DefautAttribut2 ... >
o NomElement est le nom de llment dont on dclare les attributs, NomAttribut1 est un nom dattribut. TypeAttribut1 peut avoir plusieurs valeurs diffrentes, notamment
101
une liste de la forme (A|B|...) : A,B... sont les diffrentes valeurs possibles. Exemple :
<!ATTLIST rendezVous jour (lundi|mardi|mercredi|jeudi|vendredi|samedi|dimanche) #REQUIRED>
ID : un identicateur. Quand on lutilisera dans un document la valeur utilise devra tre unique. IDREF : rfrence un identicateur dans le mme document. NMTOKEN : un nom XML bien form (une suite de lettres, de chiffres, de soulign (_ ), de : ).
DefautAttribut1 prcise si la valeur de lattribut est ncessaire ou non, et ventuellement si celui-ci a une valeur par dfaut. La chane peut tre :
Valeur par dfaut : une valeur par dfaut pour lattribut ; #REQUIRED : lattribut est ncessaire ; #IMPLIED : lattribut est optionnel.
documents valides
Un document xml est dit valide sil est bien form et quil respecte une DTD.
5.1.3 XSLT
Documentation : http://www.w3.org/TR/1999/REC-xslt-19991116. Le langage XSLT (XML Stylesheet Transformation) permet de crer un document XML, HTML ou texte partir dun document XML dorigine. Par rapport au systme CSS1 (feuilles de styles HTML), XSLT a lavantage de pouvoir modier la structure du document auquel il est appliqu. Une feuille de style XSLT est un document xml. Elle contient des rgles de formatage qui sappliquent aux diffrentes parties dun documents. La feuille XSL ci-dessous permet de produire un document html avec une la table des matires.
doc.xsl <?xml version="1.0" encoding="iso-8859-1"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > <xsl:output method="html"/> <!--
102
<xsl:template match="mondocument"> <html> <header> </header> <body> <!-- la table des matires --> <ul> <xsl:for-each select="chapitre/titre"> <li> <a href="#{generate-id(..)}"> <xsl:number count="chapitre"/><xsl:text>. </xsl:text> <xsl:value-of select="."/> </a> </li> </xsl:for-each> </ul> <!-- le corps du document --> <xsl:apply-templates/> </body> </html> </xsl:template> <!-- On transforme les titres en titres html, en crant un identificateur pour la table des matires --> <xsl:template match="titre"> <h1><a name="{generate-id(..)}"> <xsl:number count="chapitre"/> <xsl:text>. </xsl:text> <xsl:apply-templates/> </a> </h1> </xsl:template> <!-- Les commentaires ne sont pas affichs : --> <!-- Sans cette rgle, leur texte serait recopi tel quel. --> <xsl:template match="commentaire"> </xsl:template> </xsl:stylesheet>
dans le document xml mettre en forme. Les navigateurs webs rcents reconnatrons ce type de dclaration. Java permet dappliquer une feuille de style XSL un document quelconque.
103
5.2
DOM et SAX sont des spcications dAPI pour manipuler des documents XML. Selon le type de documents, et les manipulations souhaites, lune peut tre prfrable lautre.
La plupart des interfaces qui dcrivent une partie dun chier xml analys par DOM descendent de linterface org.w3c.dom.Node. Ces interfaces dnissent des mthodes en lecture et en criture. Cest--dire quil est possible de modier un document en mmoire (la classe Transformer permet ensuite ventuellement de sauver ce document). Ces interfaces sont principalement : Attr, Comment, Document, Element, Text. Tout texte balis est reprsent par un Element, y compris le document dans son ensemble. Cependant, celui-ci est inclus dans un Node de type Document.
104
Comme toutes ces interfaces descendent de Node, le mieux est de considrer les mthodes de cette interface.
Node appendChild (Node newChild) ajoute un ls ce nud NamedNodeMap getAttributes () rcupre les attributs de ce nud. Ne fonctionne que sur un Element (renvoie null pour les autres nuds). NodeList getChildNodes () rcupre la liste des enfants de ce nud. Node getNextSibling () le frre de ce nud String getNodeName () le nom de ce nud short getNodeType () le type de ce nud, parmis ATTRIBUTE_NODE CDATA_SECTION_NODE COMMENT_NODE DOCUMENT_FRAGMENT_NODE DOCUMENT_NODE DOCUMENT_TYPE_NODE ELEMENT_NODE ENTITY_NODE ENTITY_REFERENCE_NODE NOTATION_NODE PROCESSING_INSTRUCTION_NODE TEXT_NODE ( faire prcder de Node.). String getNodeValue () la valeur de ce nud Node getParentNode () le pre de ce nud boolean hasAttributes () retourne vrai si ce nud a des attributs boolean hasChildNodes () retourne vrai si ce nud a des enfants Node insertBefore (Node newChild, Node refChild) insre newChild avant refChild Node removeChild (Node oldChild) supprime oldChild void setNodeValue (String nodeValue) The value of this node, depending on its type; see the table above.
105
106
// Lit un document et laffiche de manire indente. public class TestDOM { // Utilis pour indenter laffichage. int profondeur= 0; private void dumpNode(Node node) { switch (node.getNodeType()) { case Node.ELEMENT_NODE : dumpElement((Element) node); break; case Node.TEXT_NODE : dumpText((Text) node); break; default : } } private void printSpaces() { for (int i= 0; i < profondeur; i++) System.out.print(" "); } private void print(String s) { System.out.print(s); } private void dumpElement(Element elt) { printSpaces(); print(elt.getNodeName()+ ":"); NodeList l= elt.getChildNodes(); profondeur++; for(int i= 0; i < l.getLength(); i++) { dumpNode(l.item(i)); } profondeur--; } private void dumpText(Text txt) { printSpaces(); print(txt.getData()); } public void dumpDocument(Document doc) { dumpElement(doc.getDocumentElement()); } public static void main(String[] args) throws Exception { TestDOM nav= new TestDOM(); DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance(); DocumentBuilder builder= factory.newDocumentBuilder();
107
1. cration dun analyseur SAX ; 2. cration dun builder pour le document (en tendant la classe DefaultHandler 1 .) ; 3. appel de lanalyseur sur le document.
Cration dun analyseur SAX
On doit crire une classe dont les mthodes seront appeles pour traiter les divers lments du document XML. Cette classe tend gnralement la classe DefaultHandler, qui fournit des implmentation vide des mthodes. On ne rednit normalement quune partie des mthodes.
void characters (char[] ch, int start, int length) appele pour traiter du texte. Attention, le texte traiter commence dans le tableau ch la position start, et a pour longueur length. void endDocument () appel la n du document.
1. plus prcisment org.xml.sax.helpers.DefaultHandler
108
void endElement (String namespaceURI, String localName, String qName) appel la n dun lment. Le nom de llment est stock dans localName ou qName, voire les deux, selon la conguration du systme. La distinction entre ces deux noms est lie lutilisation possible d espaces de nomages , qui sont lquivalent XML des packages java. void processingInstruction (String target, String data) appel pour traiter les instructions entre <? ... ?> void startDocument () appele au dbut du document. void startElement (String namespaceURI, String localName, String qName, Attributes atts) appele au dbut dun lment. Le nom de llment est trait soit dans localName, soit dans qName. Les attributs sont stocks dans le tableau associatif atts traitement des attributs : les attributs sont grs au travers dune interface spcique, Attributes. Les mthodes les plus importantes de cette interface sont : int getLength () renvoie le nombre dattributs. String getQName (int index) renvoie le nom du indexe attribut. String getValue (int idx) renvoie la valeur du ie attribut. String getValue (String qName) renvoie la valeur dun attribut dont on connat le nom. Utilisation de lespace de nomage Le comportement de lanalyseur est gouvern par deux proprits : http://xml.org/sax/features/namespaces et http://xml.org/sax/features/namespace-prefixes. Leur valueur peut tre consulte et xe laide des mthodes : boolean getFeature (String name) throws SAXNotRecognizedException, SAXNotSupportedException
109
Si http://xml.org/sax/features/namespace-prefixes est vrai, la valeur qName sera xe. Elle contient le nom complet de la balise, prxe par le nom de lespace de nomage. Inversement, http://xml.org/sax/features/namespace-prefixes indique que namespaceURI et localName seront renseigns.
Appel de lanalyseur sur le document
Il arrive parfois quun document XML fasse rfrence une DTD dont on ne dispose pas. Dans ce cas, les bibliothques java signaleront un problme mme si on ne demande pas de validation. En effet, la DTD nest pas utilise uniquement pour valider le document, mais aussi pour xer les valeurs de certains attributs (valeurs par dfauts), remplacer les entits par leurs valeurs... Quand on ne dispose pas de la bonne DTD, la mthode suivre est den fournir une qui soit vide. On peut rednir pour cela la mthode resolveEntity, qui sera appele lors de lanalyse pour rcuprer le code qui dcrit une entit donne (ici la dtd). Supposons que nous voulions lire un document utilisant la dtd file:///office.dtd, sans disposer de celle-ci. On pourra crire la mthode resolveEntity suivante :
public InputSource resolveEntity(String publicId, String systemId) throws SAXException { if (systemId.equals("file:///office.dtd")) { // On construit la vole une dtd vide return new InputSource(
110
} else
5.3
XSL et java
On peut utiliser des feuilles de style XSLT partir de java. Un point trs intressant est que la source et la destination de ces transformations peuvent tre, non seulement de simples chiers, mais aussi des documents DOM ou des analyseurs SAX.
TestXSL.java import javax.xml.transform.*; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; public class TestXSL { public static void main(String[] args) { try { // Objet qui reprsente la feuille de style xslt Source xslSource= new StreamSource("doc.xsl"); // Document dorigine Source xmlSource= new StreamSource("exemple.xml"); // Document html cr par le filtre Result result= new StreamResult("result.html"); // Construction du filtre XSLT : // - on rcupre un crateur de filtre TransformerFactory tfactory= TransformerFactory.newInstance(); // - on lutilise pour crer le filtre proprement dit Transformer t= tfactory.newTransformer(xslSource); // Application du filtre. Cest fini. t.transform(xmlSource, result); } catch (TransformerException e) { e.printStackTrace(); } } }
111
5.4 5.5
Quelques outils java utilisant XML Appendice : un fragment de la description du langage XML
Les documents ofciels qui dcrivent le langage XML ne sont pas forcment trs facile lire. Ils utilisent la notation Extended Backus-Naur Form (EBNF). Cette dernire emploie les oprateurs | , * , + et ? avec le mme sens que les DTD. La grande diffrence est que toute chane de caractre constante est mise entre guillemets simples. Nous allons analyser en dtail la description de la dclaration dun lment dans une DTD.
elementdecl contentspec ::= <!ELEMENT Name contentspec > ::= EMPTY | ANY | Mixed | children
children ::= (choice | seq) (? | * | +)? cp ::= (Name | choice | seq) (? | * | +)? choice ::= ( cp ( | cp )* ) seq ::= ( cp ( , cp )* ) Mixed ::= ( #PCDATA ( | Name)* )* | ( #PCDATA )
112
113
Le code suivant, par exemple, aura pour effet de dupliquer en temps rel les donnes de la table :
table= new JTable(1000,10); getContentPane().add(new JScrollPane(table)); getContentPane().add(new JScrollPane(new JTable(table.getModel())));
Linterface JTableModel dnit un certain nombre de mthodes, dont la classe AbstractTableModel fournit une implmentation par dfaut. En pratique, quand on a besoin dun modle spcique, on tend AbstractTableModel si cest possible, plutt que dimplmenter entirement JTableModel.
Class getColumnClass (int columnIndex) Retourne la classe correspondant aux entres de la colonne. AbstractTableModel, la valeur retourne est toujours Object.class.
Dans
114 int getColumnCount () retourne le nombre de colonnes. String getColumnName (int columnIndex) retourne le nom de la colonne i. int getRowCount () retourne le nombre de lignes.
Object getValueAt (int rowIndex, int columnIndex) retourne la valeur de la cellule rowIndex, columnIndex. boolean isCellEditable (int rowIndex, int columnIndex) retourne vrai si la case rowIndex, columnIndex est ditable. void setValueAt (Object aValue, int rowIndex, int columnIndex) xe la valeur de la case rowIndex, columnIndex. void addTableModelListener (TableModelListener l) ajoute un observateur, qui sera prvenu quand le modle sera modi. void removeTableModelListener (TableModelListener l) supprime un observateur.
La classe AbstractTableModel propose de plus des mthodes pour prvenir les observateurs :
void fireTableCellUpdated (int ligne, int colonne) avertit les observateurs que la cellule ligne, colonne a t modie. void fireTableDataChanged () avertit les observateurs que les donnes de la table ont chang. void fireTableRowsDeleted (int firstRow, int lastRow) avertit les observateurs que les ranges dindices compris entre i0 et i1, inclus, ont t supprimes. void fireTableRowsInserted (int firstRow, int lastRow) avertit les observateurs que les ranges dindices compris entre i0 et i1, inclus, ont t insres void fireTableRowsUpdated (int i0, int i1) avertit les observateurs que les ranges dindices compris entre i0 et i1, inclus, ont t modies. void fireTableStructureChanged () avertit les observateurs que la structure de la table a chang. void fireTableChanged (TableModelEvent e) Expdie lvnement e qui reprsente les modications faites la table tous ses observateurs. Gnralement, on utilisera plutt les mthodes prcdentes.
115
6.2
Le design pattern commande est utilis plusieurs reprises dans swing, des ns diffrentes.