cours java avance avancé thread arraylist
DESCRIPTION
cours java facile avancé l'essentiel du cours par Mr Oussama El Ghoul Easy JavaTRANSCRIPT
Chapitre 1: Les threads
Mr. Oussama El Ghoul
• En Java, un thread est un objet d’une classe qui dispose d’une méthode nommée run qui sera exécutée lorsque le thread sera démarré.
• Il existe deux façons de définir une telle classe. – créer une classe dérivée de la classe Thread – Implémenter l’interface Runnable
Exemple introduc2f class Ecrit extends Thread { public Ecrit(String texte, int nb){ this.texte = texte ; this.nb = nb ; } public void run () { for(inO=0;i<nb;i++) System.out.print (texte) ; }
}
Exemple introduc2f
La créaOon des objets threads pourra se faire, depuis n’importe quel endroit du programme (par exemple depuis une méthode main) de ceUe façon : Ecrit e1 = new Ecrit ("bonjour", 10) ; Ecrit e2 = new Ecrit ("bonsoir", 12) ; Ecrit e3 = new Ecrit ("\n", 5) ; Le lancement de l’exécuOon du thread se fait en appelant la méthode start de la classe Thread, par exemple : e1.start() ; // lance l’exécu2on du thread e1 les textes apparaissent plus ou moins entremêlés à l’écran.
Exemple introduc2f
• nous uOliserons l’appel sleep(t) où t est un nombre de millisecondes.
• Cet appel demande que le thread correspondant soit arrêté (on dira "mis en sommeil") pour au moins la durée menOonnée.
• CeUe démarche laisse ainsi la possibilité à d’autres threads de s’exécuter à leur tour.
• La méthode sleep est suscepOble de générer une excepOon de type InterruptedExcep0on.
Exemple introduc2f
Exemple introduc2f
Remarques • Un programme comporte toujours au moins un thread dit "thread principal" correspondant tout simplement à la méthode main. Ici, notre programme comporte donc quatre threads et non trois. Lorsque la méthode sleep est appelée, elle permet de donner la main à l’un des autres threads, y compris le thread principal.
• Si l’on appelait directement la méthode run de nos objets threads, le programme foncOonnerait mais l’on n’aurait plus affaire à trois threads différents. On exécuterait alors enOèrement la méthode run du premier, puis celle du deuxième et enfin celle du troisième, tout cela dans un seul thread.
Remarques
• La méthode start ne peut être appelée qu’une seule fois pour un objet thread donné. Dans le cas contraire, on obOendra une excepOon IllegalThreadStateExcep8on.
• La méthode sleep est en fait une méthode staOque (de la classe Thread ) qui met en sommeil le thread en cours d’exécuOon. Nous aurions pu remplacer l’appel sleep (a;ente) par Thread.sleep (a;ente).
• Si nous ne prévoyons pas d’appel de sleep dans notre méthode run, le programme foncOonnera encore mais son comportement dépendra de l’environnement.
U2lisa2on de l’interface Runnable
• Nous venons de voir comment créer des threads à parOr de la classe Thread.
• CeUe démarche est simple mais elle présente une lacune : les objets threads ne peuvent pas dériver d’autre chose que de Thread (puisque Java ne possède pas d’héritage mulOple).
• Un thread peut être créé grâce à une classe implémentant l’interface Runnable, qui comporte une seule méthode nommée run.
Exemple
Exemple • Nous serons amenés à créer des objets de type Ecrit, par exemple :
Ecrit e1 = new Ecrit ("bonjour ", 10, 5) ; • CeUe fois, ces objets ne sont plus des threads et ne peuvent donc plus être lancés par la méthode start.
• Nous devrons tout d’abord créer des objets de type Thread en uOlisant une forme parOculière de constructeur recevant en argument un objet implémentant l’interface Runnable, par exemple :
Thread t1 = new Thread (e1) ; • Nous lancerons ensuite classiquement ce thread par start :
t1.start() ;
Exemple
Interrup2on d’un thread
• Dans certains cas, on peut avoir besoin d’interrompre prématurément un thread depuis un autre thread.
• Ce besoin peut devenir fondamental dans le cas de ce que nous nommerons des "threads infinis", c’est-‐à-‐dire dans lesquels la méthode run n’a pas de fin programmée ;
• ce pourrait être le cas d’un thread de surveillance d’appels dans un serveur Web.
Interrup2on d’un thread
• Java dispose d’un mécanisme permeUant à un thread d’en interrompre un autre.
• La méthode interrupt de la classe Thread demande à l’environnement de posiOonner un indicateur signalant une demande d’arrêt du thread concerné.
Interrup2on d’un thread
Interrup2on d’un thread
Interrup2on d’un thread
• La méthode staOque interrupted de la classe Thread remet à false l’indicateur de demande d’arrêt.
• La classe Thread dispose également d’une méthode (non staOque) isInterrupted qui examine l’indicateur de l’objet thread correspondant (et non plus du thread courant), sans en modifier la valeur.
Threads démons et arrêt brutal
• il existe deux catégories de threads : – les threads dits uOlisateur, – les threads dits démons.
• La parOcularité d’un thread démon est la suivante : – si à un moment donné, les seuls threads en cours d’exécuOon d’un même programme sont des démons, ces derniers sont arrêtés brutalement et le programme se termine.
Threads démons et arrêt brutal
• Par défaut, un thread est créé dans la catégorie du thread qui l’a créé (uOlisateur pour main, donc pour tous les threads, tant qu’on n’a rien demandé d’autre).
• Pour faire d’un thread un démon, on effectue l’appel setDaemon(true) avant d’appeler la méthode start (si on le fait après ou si l’on appelle plusieurs fois setDaemon, on obOent une excepOon InvalidThread-‐StateExcep8on).
Coordina2on de threads
• L’avantage des threads sur les processus est qu’ils apparOennent à un même programme.
• Ils peuvent donc éventuellement partager les mêmes objets.
• Cet avantage s’accompagne parfois de contraintes: – il faudra éviter que deux threads puissent accéder (presque) en même temps au même objet.
– un thread devra aUendre qu’un autre ait achevé un certain travail sur un objet avant de pouvoir lui-‐même poursuivre son exécuOon.
Coordina2on de threads : exemple
• Prenons un exemple simple de deux threads répétant indéfiniment les acOons suivantes : – incrémentaOon d’un nombre et calcul de son carré (premier thread),
– affichage du nombre et de son carré (second thread). • On voit que si le premier thread se trouve interrompu entre l’incrémentaOon et le calcul de carré, le second risque d’afficher le nouveau nombre et l’ancien carré.
• Pour pallier ceUe difficulté, Java permet de déclarer des méthodes avec le mot-‐clé synchronized.
• À un instant donné, une seule méthode ainsi déclarée peut être appelée pour un objet donné.
Coordina2on de threads : exemple
Coordina2on de threads : exemple
Coordina2on de threads : exemple
Coordina2on de threads : No2on de verrou
• À un instant donné, une seule méthode synchronisée peut donc accéder à un objet donné.
• Pour meUre en place une telle contrainte, on peut considérer que, pour chaque objet doté d’au moins une méthode synchronisée, l’environnement gère un "verrou" (ou une clé) unique permeUant l’accès à l’objet.
• Le verrou est aUribué à la méthode synchronisée appelée pour l’objet et il est resOtué à la sorOe de la méthode. Tant que le verrou n’est pas resOtué, aucune autre méthode synchronisée ne peut le recevoir (bien sûr, les méthodes non synchronisées peuvent, quant à elles, accéder à tout moment à l’objet).
Coordina2on de threads : No2on de verrou
Coordina2on de threads : Interblocage
• L’uOlisaOon des verrous sur des objets peut conduire à une situaOon de blocage qui peut se définir ainsi : – le thread t1 possède le verrou de l’objet o1 et il aUend le verrou de l’objet o2,
– le thread t2 possède le verrou de l’objet o2 et il aUend le verrou de l’objet o1.
Coordina2on de threads : ATente et no2fica2on
• Comme nous l’avons dit, il arrive que l’on ait besoin de coordonner l’exécuOon de threads,
• Un thread devant aUendre qu’un autre ait effectué une certaine tâche pour conOnuer son exécuOon.
Coordina2on de threads : ATente et no2fica2on
• Java offre un mécanisme basé sur l’objet et sur les méthodes synchronisées que nous venons d’étudier : – une méthode synchronisée peut appeler la méthode wait de l’objet dont elle possède le verrou, ce qui a pour effet : • de rendre le verrou à l’environnement qui pourra, le cas échéant, l’aUribuer à une autre méthode synchronisée,
• de meUre "en aUente" le thread correspondant ; plusieurs threads peuvent être en aUente sur un même objet ; tant qu’un thread est en aUente, l’environnement ne lui donne pas la main ;
– une méthode synchronisée peut appeler la méthode no8fyAll d’un objet pour prévenir tous les threads en aUente sur cet objet et leur donner la possibilité de s’exécuter.
Coordina2on de threads : Exercice 1
• Ecrire un programme qui gère une "réserve" (de tout ce qui se dénombre). Il comporte : – un thread qui ajoute une quanOté donnée, – deux threads qui puisent chacun une quanOté donnée.
• Un thread ne peut puiser dans la réserve que si elle conOent une quanOté suffisante.
• La réserve est représentée par un objet r, de type Reserve. • CeUe classe dispose de deux méthodes synchronisées puise et ajoute.
• Lorsque la méthode puise s’aperçoit que la réserve est insuffisante, il appelle wait pour meUre le thread correspondant en aUente. Parallèlement, la méthode ajoute appelle no8fyAll après chaque ajout.
Coordina2on de threads : Exercice 2
• Dans l’exemple de calcul et d’affichage du carré, les deux threads calc et aff n’étaient pas coordonnés ; on pouvait incrémenter plusieurs fois le nombre avant qu’il n’y ait affichage ou, encore, afficher plusieurs fois les mêmes informaOons.
• Ici, nous allons faire en sorte que, malgré leurs rythmes différents, les deux threads soient coordonnés, c’est-‐à-‐dire qu’on effectue alternaOvement une incrémentaOon et un calcul.
Chapitre 2: La programmaOon générique
IntroducOon
• On parle généralement de programmaOon générique lorsqu’un langage permet d’écrire un code source unique uOlisable avec des objets ou des variables de types quelconques.
• On peut prendre l’exemple d’une méthode de tri applicable à des objets de type quelconque ou encore celui d’une classe permeUant de manipuler des ensembles d’objets de type quelconque.
No2on de classe générique
• La définiOon d’une classe générique pourrait se présenter ainsi :
No2on de classe générique: déclara2on
• On note la présence d’un "paramètre de type" nommé ici T, dans : class Couple<T>
• Il sert à préciser que, dans la définiOon de classe qui suit, T représente un type quelconque.
• Ce paramètre T peut alors être uOlisé là où un type précis peut l’être normalement.
• Ici, on le rencontre : – dans les déclaraOons des champs x et y, – dans l’en-‐tête du constructeur et de la méthode getPremier.
No2on de classe générique: u2lisa2on
• Lors de la déclaraOon d’un objet de type Couple, on devra préciser le type effecOf correspondant à T, de ceUe manière : – Couple<Integer>ci; – Couple<Point> cp;
• La seule contrainte à respecter à ce niveau est que ce type doit obligatoirement être une classe ;
• la déclaraOon suivante serait rejetée : – Couple <int> c ; // erreur : int n’est pas une classe
• L’appel du constructeur devra également préciser le type voulu. Par exemple, si l’on dispose de deux objets oi1 et oi2 de type Integer, on créera le couple correspondant par : – ci = new Couple<Integer> (oi1, oi2) ;
No2on de classe générique: compila2on
• Considérons notre classe Couple <T>. Sa compilaOon conduit à créer les mêmes "byte codes" que si nous l’avions définie ainsi (en supprimant la déclaraOon de paramètre de type <T> et en remplaçant T par Object dans la suite) :
No2on de classe générique: compila2on
• Dans l’uOlisaOon de la classe générique Couple<T>, nous avions déclaré : Couple <Integer> ci ;
• Lorsque le compilateur rencontre un appel tel que : ci.getPremier()
• il le traduit en insérant une conversion du type Object dans le type Integer. En effet, à ce niveau : – le compilateur sait que, à cause de l’effacement, lors de l’exécuOon, le résultat fourni par getPremier sera de type Object,
– mais il connaît quand même le type de ci, grâce à sa déclaraOon. En définiOve, tout se passera comme si vous aviez écrit votre appel de ceUe façon : (Integer) ci.getPremier()
No2on de classe générique
• Il est possible, au moment de la définiOon de la classe, d’imposer certaines contraintes.
• On pourra imposer à la classe correspondant à un paramètre de type d’être dérivée d’une classe donnée ou d’implémenter une ou plusieurs interfaces.
• Par exemple: class Couple <T extends Number> { // définiOon précédente inchangée } • on imposera au type désigné par T de dériver de la classe Number
No2on de classe générique: limita2ons
• On ne peut pas instancier un objet d’un type paramétré
No2on de classe générique: limita2ons
• On ne peut pas instancier de tableaux d’éléments d’un type générique
• Seul le type brut est connu lors de l’exécuOon
No2on de classe générique: limita2ons
• ExcepOons – Il n’est pas possible de créer une classe générique dérivée de Throwable, donc a forOori de Excep8on ou de Error :
– Il n’est pas possible de lever une excepOon (throw) à l’aide d’un objet d’une classe générique :
No2on de classe générique: limita2ons
• Champs staOques – Si l’on définit un champ staOque dans une classe générique, il sera unique pour toutes les instances de ceUe classe, quelle que soit la valeur du paramètre de type.
– Un champ staOque ne peut pas être d’un type paramétré :
No2on de méthodes générique
• Nous venons de voir comment on pouvait introduire des paramètres de type dans une classe.
• La même démarche peut s’appliquer à une méthode ; on parle alors tout naturellement de "méthodes génériques".
No2on de méthodes générique: exemple
• Là encore, la compilaOon de la méthode générique conduit à l’effacement du type T, exactement comme si l’on avait défini hasard de ceUe façon :
No2on de méthodes générique: exemple
• Si l’on souhaite davantage de vérificaOons à la compilaOon, il est possible d’imposer le type voulu pour T lors de l’appel de la méthode, en uOlisant une syntaxe de la forme suivante :
nomClasse<type>.nomMéthode
Héritage et programma2on générique
• Disposant d’une classe générique telle que : class C <T> { ..... } • il existe bon nombre de façons d’en créer des classes dérivées : – La classe dérivée conserve les paramètres de type de la classe de base, sans en ajouter d’autres, comme dans :
class D <T> extends C <T> { ..... } class D<T, U> extends C<T, U> – Ici, C et D uOlisent le même paramètre de type.
Héritage et programma2on générique
• La classe dérivée uOlise les mêmes paramètres de type que la classe de base, en en ajoutant de nouveaux, comme dans :
class D <T, U> extends C <T> { ..... } • La classe dérivée introduit des limitaOons sur un ou plusieurs des paramètres de type de la classe de base, comme dans :
class D <T extends Number> extends C<T>
Héritage et programma2on générique
• La classe de base n’est pas générique, la classe dérivée l’est, comme dans (on suppose que X est une classe) :
class D<T> extends X • La classe de base est une instance parOculière d’une classe générique, comme dans :
class D<T> extends C<String> • En revanche, ces situaOons seront incorrectes : class D extends C<T> //erreur: D doit disposer au moins du paramètre T class G<T> extends C<T extends Number> // erreur
Les jokers
• si T’ dérive de T, C<T’> ne dérive pas de C<T>. • il existe une relaOon intéressante entre ces deux classes puisqu’il reste toujours possible d’uOliser un objet de type C<T’> comme on le ferait d’un objet de type C<T>,
Les jokers • Avec notre classe C<T> précédente, nous pouvons bien entendu
définir : C<Integer> ci ; C<Double> cd ; • Mais, on peut également définir : C<?>cq; //cq désigne un couple d’éléments d’un type quelconque • Certes, ceUe déclaraOon ressemble à : C<Object> cq1 ; • Mais, la grande différence est que, par exemple, ceUe affectaOon
devient légale cq = cd ; // OK : affecta2on d’un C<Double> à un C<?> • alors que celle-‐ci ne le serait pas : cq1 = cd ; // erreur de compila2on • En revanche, il n’est pas possible de modifier l’objet référence par cq :
cq.setT(.....); //erreur de compilaOon
Les jokers • ce ne sont pas simplement les modificaOons d’un objet de type C<?> qui sont interdites, mais plus précisément, l’appel de toute méthode recevant un argument du type correspondant à ?.
• Par exemple, supposons qu’on ait muni notre classe C d’une méthode permeUant de comparer un aUribut x à un objet fourni en argument
•
Les jokers • On peut imposer des limitaOons à un joker, comme on le fait pour des paramètres de type.
• Ainsi, avec notre classe C nous pouvons définir : C <Object> co ; C <Integer> ci ; C <? extends Number> cqn ; // ? représente un type quelconque dérivé de Number • L’affectaOon suivante sera illégale : cqn = co ; // erreur de compila2on : Object ne dérive pas de Number • tandis que celle-‐ci sera légale : cqn = ci ; // OK : Integer dérive bien de Number
Chapitre 3: Les collecOons
Introduc2on
• La version 2 de Java a élargi et harmonisé la bibliothèque de classes uOlitaires (java.u8l).
• On y trouve désormais des classes permeUant de manipuler les principales structures de données, c’est-‐à-‐dire les vecteurs dynamiques, les ensembles, les listes chaînées, les piles et les tables associaOves.
Les collec2ons
• les collec2ons sont manipulées par le biais de classes génériques implémentant l’interface Collec0on<E>, E représentant le type des éléments de la collecOon.
• Tous les éléments d’une même collecOon sont donc de même type E (ou, à la rigueur, d’un type dérivé de E).
• Ainsi, à une liste chaînée LinkedList<String>, on ne pourra pas ajouter des éléments de type Integer ou Point.
Ordre des éléments d’une collecOon
• Par nature, certaines collecOons, comme les ensembles, sont dépourvues d’un quelconque ordonnancement de leurs éléments.
• D’autres, en revanche, comme les vecteurs dynamiques ou les listes chaînées voient leurs éléments naturellement ordonnés suivant l’ordre dans lequel ils ont été disposés.
• Dans de telles collecOons, on pourra toujours parler, à un instant donné, du premier élément, du deuxième, ... du nième, du dernier.
Ordre des éléments d’une collecOon
• Indépendamment de cet ordre naturel, on pourra, dans certains cas, avoir besoin de classer les éléments à par2r de leur valeur.
• Ce sera par exemple le cas d’un algorithme de recherche de maximum ou de minimum ou encore de tri.
• Lorsqu’il est nécessaire de disposer d’un tel ordre sur une collecOon, les méthodes concernées considèrent par défaut que ses éléments implémentent l’interface Comparable (Comparable<E> depuis le JDK 5.0) et recourent à sa méthode compareTo
Ordre des éléments d’une collecOon
• Il se peut que la démarche précédente (uOlisaOon de compareTo) ne convienne pas.
• Ce sera notamment le cas lorsque : – les éléments sont des objets d’une classe existante qui n’implémente pas l’interface Comparable,
– on a besoin de définir plusieurs ordres différents sur une même collecOon.
Ordre des éléments d’une collecOon
• Pour ce faire, on fournit en argument (du constructeur ou de l’algorithme) un objet qu’on nomme un comparateur.
• Il s’agit en fait d’un objet d’un type implémentant l’interface Comparator<E>1 (ou Comparator avant le JDK 5.0) qui comporte une seule méthode : – public int compare (E o1, E o2) // depuis le JDK 5.0 – public int compare (Object o1, Object o2) // avant le JDK 5.0
égalité des éléments d’une collecOon
• Toutes les collec2ons nécessitent de définir l’égalité de deux éléments.
• Ce besoin est évident dans le cas des ensembles (HashSet et TreeSet) dans lesquels un même élément ne peut apparaître qu’une seule fois.
Ordre des éléments d’une collecOon
• CeUe égalité est définie en recourant à la méthode equals de l’objet.
• Pour des éléments de type String, File ou d’une classe enveloppe, les choses seront naturelles puisque leur méthode equals se base réellement sur la valeur des objets.
• Pour les autres, par défaut, leur méthode equals est celle héritée de la classe Object.
• Elle se base simplement sur les références : deux objets différents apparaîtront toujours comme non égaux (même s’ils conOennent exactement les mêmes valeurs).
• Pour obtenir un comportement plus saOsfaisant, il faudra alors redéfinir la méthode equals de façon appropriée.
Les itérateurs et leurs méthodes
• Les itérateurs sont des objets qui permeUent de "parcourir" un par un les différents éléments d’une collecOon
• Il existe deux sortes d’itérateurs : – monodirec0onnels : le parcours de la collecOon se fait d’un début vers une fin ; on ne passe qu’une seule fois sur chacun des éléments ;
– bidirec0onnels : le parcours peut se faire dans les deux sens ; on peut avancer et reculer à sa guise dans la collecOon.
Les itérateurs monodirec2onnels : l’interface Iterator
• Chaque classe collecOon dispose d’une méthode nommée iterator fournissant un itérateur monodirecOonnel, c’est-‐à-‐dire un objet d’une classe implémentant l’interface Iterator<E> (Iterator avant le JDK 5.0).
• Associé à une collecOon donnée, il possède les propriétés suivantes : – À un instant donné, un itérateur indique ce que nous nommerons une posi0on
courante désignant soit un élément donné de la collecOon, soit la fin de la collecOon
– On peut obtenir l’objet désigné par un itérateur en appelant la méthode next de l’itérateur, ce qui, en outre, avance l’itérateur d’une posiOon.
– La méthode hasNext de l’itérateur permet de savoir si l’itérateur est ou non en fin de collecOon
Les itérateurs monodirec2onnels : l’interface Iterator
• Canevas de parcours d’une collec2on
Les itérateurs monodirec2onnels : l’interface Iterator
• La méthode remove de l’interface Iterator
• Notez bien que remove ne travaille pas directement avec la posiOon courante de l’itérateur, mais avec la dernière référence renvoyée par next que nous nommerons objet courant. Alors que la posiOon courante possède toujours une valeur, l’objet courant peut ne pas exister.
Les itérateurs bidirec2onnels : l’interface Iterator
• Les itérateurs bidirec2onnels : l’interface ListIterator • Certaines collecOons (listes chaînées, vecteurs dynamiques) peuvent, par nature,
être parcourues dans les deux sens. • Elles disposent d’une méthode nommée listIterator qui fournit un itérateur
bidirecOonnel. • Il s’agit, ceUe fois, d’objet d’un type implémentant l’interface ListIterator<E>
(dérivée de Iterator<E>). • Il dispose bien sûr des méthodes next, hasNext et remove héritées de Iterator. Mais
il dispose aussi d’autres méthodes permeUant d’exploiter son caractère bidirecOonnel, à savoir : – comme on peut s’y aUendre, des méthodes previous et hasPrevious, complémentaires de next et
hasNext, – mais aussi, des méthodes d’addiOon1 d’un élément à la posiOon courante (add) ou de modificaOon
de l’élément courant (set).
Les itérateurs bidirec2onnels : l’interface Iterator
• L’interface ListIterator prévoit une méthode add qui ajoute un élément à la posiOon courante de l’itérateur.
• Si ce dernier est en fin de collecOon, l’ajout se fait tout naturellement en fin de collecOon (y compris si la collecOon est vide).
• Si l’itérateur désigne le premier élément, l’ajout se fera avant ce premier élément.
Les itérateurs bidirec2onnels : l’interface Iterator
• L’appel set (elem) remplace par elem l’élément courant, c’est-‐à-‐dire le dernier renvoyé par next ou previous, à condiOon que la collecOon n’ait pas été modifiée entre temps (par exem-‐ ple par add ou remove).
• N’oubliez pas que les éléments ne sont que de simples références ; la modificaOon opérée par set n’est donc qu’une simple modificaOon de référence (les objets concernés n’étant pas modifiés).
• N’oubliez pas que set, comme remove, s’applique à un élément courant (et non comme add à une posiOon courante).
• Par exemple, si it est un itérateur bidirecOonnel, la séquence suivante est incorrecte et provoquera une excepOon IllegalStateExcep8on :
it.next() ; it.remove() ; it.set (el) ;
Opéra2ons communes à toutes les collec2ons
• Construc2on
Opéra2ons communes à toutes les collec2ons
• Opéra2ons communes à toutes les collec2ons • Ajout
Opéra2ons communes à toutes les collec2ons
• Opéra2ons communes à toutes les collec2ons • La méthode add fournit la valeur true lorsque l’ajout a pu être réalisé, ce
qui sera le cas avec la plupart des collecOons, excepOon faite des ensembles ; dans ce cas, on obOent la valeur false si l’élément qu’on cherche à ajouter est déjà "présent" dans l’ensemble.
• De la même façon, toute collecOon dispose d’une méthode remove (element) qui recherche un élément de valeur donnée et le supprime s’il existe en fournissant alors la valeur true.
• La méthode size fournit la taille d’une collecOon, c’est-‐à-‐dire son nombre d’éléments tandis que la méthode isEmpty teste si elle est vide ou non. La méthode clear supprime tous les éléments d’une collecOon.
• La méthode contains (elem) permet de savoir si la collecOon conOent un élément de valeur égale à elem
Les itérateurs monodirec2onnels : l’interface Iterator
Les itérateurs monodirec2onnels : l’interface Iterator
Les listes chaînées -‐ classe LinkedList
• LinkedList dispose des méthodes spécifiques getFirst et getLast fournissant respecOvement le premier ou le dernier élément de la liste.
Les listes chaînées -‐ classe LinkedList
• La méthode add de ListIterator (ne la confondez pas avec celle de Collec8on) permet d’ajouter un élément à la posiOon courante.
• la méthode add prévue dans l’interface Collec8on reste uOlisable. Elle se con-‐ tente d’ajouter l’élément en fin de liste.
• la méthode remove de ListIterator supprime le dernier élément renvoyé soit par next
• La classe LinkedList dispose en outre de méthodes spécifiques removeFirst et removeLast qui suppriment le premier ou le dernier élément de la liste.
• comme pour toute collecOon, supprimer d’une liste un élément de valeur donnée avec remove (element).
Les vecteurs dynamiques -‐ classe ArrayList
• Comme toute collecOon, les vecteurs disposent de la méthode add (elem) qui se contente d’ajouter l’élément elem en fin de vecteur.
• On peut aussi ajouter un élément elem en un rang i donné à l’aide de la méthode add (i,elem).
• La classe ArrayList dispose d’une méthode spécifique remove permeUant de supprimer un élément de rang donné
• On peut connaître la valeur d’un élément de rang i par get(i). • On peut remplacer par elem la valeur de l’élément de rang i par set (i,
elem).
Les ensembles • Deux classes implémentent la noOon d’ensemble : HashSet et TreeSet. • Théoriquement, un ensemble est une collecOon non ordonnée
d’éléments, aucun élément ne pouvant apparaître plusieurs fois dans un même ensemble.
• HashSet qui recourt à une technique dite de hachage, ce qui conduit à une efficacité du test d’appartenance en O(1),
• TreeSet qui uOlise un arbre binaire pour ordonner complètement les éléments, ce qui conduit à une efficacité du test d’appartenance en O(Log N).
• La seule façon d’ajouter un élément à un ensemble est d’uOliser la méthode add prévue dans l’interface Collec8on.
Les queues • Le JDK 5.0 a introduit une nouvelle interface Queue (dérivée
elle aussi de Collec8on), desOnée à la gesOon des files d’aUente (ou queues).
• Il s’agit de structures dans lesquelles on peut : – introduire un nouvel élément, si la queue n’est pas pleine, – prélever le premier élément de la queue,
• Le prélèvement du premier élément de la queue peut se faire : – de façon destrucOve, à l’aide de la méthode poll : l’élément ainsi prélevé est
supprimé de la queue ; la méthode renvoie null si la queue est vide, – de façon non destrucOve à l’aide de la méthode peek.
Les queues
• Deux classes implémentent l’interface Queue : – La classe LinkedList, – La classe PriorityQueue, introduite par Java 5, permet de choisir une relaOon d’ordre ; le type des éléments doit implémenter l’interface Comparable ou être doté d’un comparateur approprié. Les éléments de la queue sont alors ordonnés par ceUe relaOon d’ordre et le prélèvement d’un élément porte alors sur le "premier" au sens de ceUe relaOon (on parle du "plus prioritaire", d’où le nom de PriorityQueue).
Les tables associa2ves
• Une table associaOve permet de conserver une informaOon associant deux parOes nommées clé et valeur.
• On va donc tout naturellement retrouver les deux types d’organisaOon rencontrés pour les ensembles : – table de hachage : classe HashMap, – arbre binaire : classe TreeMap.
Les tables associa2ves
Les tables associa2ves
Les tables associa2ves
L’introspecOon
Les bases de l’introspec2on : le type Class
• Jusqu’ici, nous avons manipulé des objets qui étaient des instances d’une classe.
• Mais Java permet également de manipuler des classes, qu’il considère alors comme des objets d’un "super-‐type" nommé Class.
• Nous verrons bientôt qu’un tel super-‐ type dispose de méthodes fournissant des informaOons sur ses instances qui sont des "super-‐ objets »
Les bases de l’introspec2on : le type Class
• DéclaraOon d’instances du type Class – Avant Java 5, : Class c ; – Depuis Java 5, le type Class est devenu générique, de sorte qu’il doit être paramétré : • Class<Point> c ; (nous verrons toutefois que ceUe démarche doit généralement être évitée) ; • Class<?> c ; • Class<? extends Point> c ;
Les bases de l’introspec2on : le type Class
• Nous pouvons exploiter le fait que toute classe T dispose d’un champ staOque public, nommé class, fournissant le "super-‐objet" de type Class correspondant à ceUe classe T
• Class c<?> ; // ou, avant Java 5 : Class c ; • c = Point.class ; • affecte à c le super-‐objet représentant la classe Point.
Les bases de l’introspec2on : le type Class
• Toute classe dispose d’une méthode getClass qui permet de retrouver la classe d’une instance donnée.
• Point p ; • c = p.getClass() ; • on affecte à c, ce même super-‐objet représentant la classe Point.
Les bases de l’introspec2on : le type Class
• Toute classe dispose d’une méthode getClass qui permet de retrouver la classe d’une instance donnée.
• Point p ; • c = p.getClass() ; • on affecte à c, ce même super-‐objet représentant la classe Point.
Les bases de l’introspec2on : le type Class
• La classe Class dispose d’une méthode nommée getName fournissant le nom (String) d’un super-‐objet, donc d’une classe.
• Avec notre précédent exemple : Class<?>c; Pointp; ..... c = p.getClass() ; • l’expression c.getName() aurait comme valeur, la chaîne "Point".
Les bases de l’introspec2on : le type Class
• Exemple
Les bases de l’introspec2on : le type Class
• Exemple
Accès aux informa2ons rela2ves à une classe
• Généralités : les types Field, Method et Constructor • voyons comment nous pouvons en extraire des informaOons, à l’aide des méthodes du type Class (définies dans le package Java.lang.reflect)
• D’une manière générale, ces méthodes uOlisent : – des objets de type Field pour représenter un champ, – des objets de type Method pour représenter une méthode (constructeurs non compris),
– des objets de type générique Constructor <T> (Constructor avant le JDK 5) pour représenter un constructeur.
Accès aux informa2ons rela2ves à une classe
• la méthode getDeclaredFields fournit un tableau d’objets de type Field correspondant aux champs déclarés. La méthode GetFields fournirait tous les champs publics, y compris ceux hérités.
Field[] champs = c.getDeclaredFields() ; • la méthode getDeclaredMethods fournit un tableau d’objets
de type Method: • Method[] methodesd = c.getDeclaredMethods () ; • La méthode getMethods fournirait toutes les méthodes
publiques, y compris celles héritées.
Accès aux informa2ons rela2ves à une classe
• la méthode getDeclaredConstructors fournit un tableau d’objets de type Constructor,
• Constructor <?>[] constructeurs = c.getDeclaredConstructors() ;
• La méthode getConstructors fournirait seulement les constructeurs publics.
Accès aux informa2ons rela2ves à une classe
• Dans la classe Field, on trouve, en plus de getName, un certain nombre de méthodes permeUant d’obtenir des informaOons sur le champ concerné – la méthode getType fournit le type du champ, sous forme d’un objet
de type Class<?> ; on notera que les types de base disposent eux-‐aussi d’un tel type, par exemple Class<int> ou Class<double>;
– la méthode getModifiers fournit un enOer dont la valeur dépend des modificateurs (public, private, protected, sta8c...) ;
– la classe Modifier propose des méthodes (isPublic, isPrivate, isSta8c...) permeUant de savoir si un modificateur donné est présent dans l’enOer précédent.
Accès aux informa2ons rela2ves à une classe
• Dans les classes Method et Construtor, on trouve, en plus de getName, un certain nombre de méthodes permeUant d’obtenir des informaOons sur la méthode concernée. – la méthode getModifiers fournit un enOer dont la valeur dépend des
modificateurs (public, private, protected, sta8c, synchronized, final...) ; – la classe Modifier propose des méthodes (isPublic, isPrivate, isSta8c...)
permeUant de sa-‐ voir si un modificateur donné est présent dans l’enOer précédent ;
– la méthode getReturnType fournit un objet de type Class<?> correspondant au type de la valeur de retour (bien entendu, ceUe méthode n’existe pas dans la classe Constructor) ;
– la méthode getParameterTypes fournit un tableau d’objet de type Class<?> correspondant aux types des différents arguments ;
– la méthode getExcep8onType fournit un tableau d’éléments de type Class <?> correspondant aux types des excepOons levées par la méthode.
Les annota2ons
• Voyons comment définir et uOliser une annotaOon simple (ne comportant pas de paramètres) qui sera donc simplement caractérisée par le nom que nous lui donnerons, ici Marque. Sa définiOon se présentera comme ceci :
• public @interface Marque {} • Elle ressemble à celle d’une interface (en uOlisant le terme @interface au lieu de interface), avec un statut (public, private, protected ou rien), un nom et un corps
Les annota2ons
Les annota2ons
• L’annotaOon de l’exemple précédent était très simple car réduite à une simple marque. Mais une annotaOon peut disposer de un ou plusieurs paramètres (appelés aussi "aUributs"). Voyez cet exemple :
public @interface InformaOons { String message () ; int annee () ; }
Les annota2ons
• Il suffira de lui fournir les valeurs des paramètres requis de ceUe manière :
@InformaOons (message = "code provisoire", annee = 2007) public class A { ..... } • on peut fournir des valeurs par défaut pour tout ou parOe
des paramètres, comme dans : public @interface InformaOons { String message () default "" ; // message vide par défaut int annee () ; // pas de valeur par défaut }
Exploita2on des annota2ons par introspec2on
• isAnnota8onPresent qui teste si une annotaOon donnée (considérée comme une classe) fournie en argument est présente, par exemple :
• if (A.class.isAnnotaOonPresent (Infos.class)) • examine si l’annotaOon @Infos (classe de nom Infos) est présente sur la classe A ;
Exploita2on des annota2ons par introspec2on
• getAnnota8on qui reçoit en argument la classe de l’annotaOon recherchée et qui fournit en résultat un objet du type de l’annotaOon en quesOon ;
• par exemple, si m1 est un objet de type Method, avec : • Infos ainf1 = m1.getAnnotaOon(Infos.class) ;
• getAnnota8ons qui fournit un tableau d’objets de type Annota8on correspondant aux annotaOons de l’élément concerné, y compris celles héritées,
• getDeclaredAnnota8ons qui fournit les annotaOons effecOvement déclarées.
Exploita2on des annota2ons par introspec2on
Exploita2on des annota2ons par introspec2on
Source
• Programmer en Java : Claude delannoy