Vous êtes sur la page 1sur 154

Cours POO Java

Séances 4, 5, 6
Laurent Henocque
http://laurent.henocque.free.fr/
Enseignant Chercheur ESIL/INFO France
http://laurent.henocque.perso.esil.univmed.fr/
mis à jour en Septembre 2009
Licence Creative
Commons

Cette création est mise à disposition selon le Contrat Paternité-Partage


des Conditions Initiales à l'Identique 2.0 France disponible en ligne

http://creativecommons.org/licenses/by-sa/2.0/fr/

ou par courrier postal à Creative Commons, 559 Nathan Abbott Way,


Stanford, California 94305, USA.
Avertissement

• Ce support est la deuxième partie d’un cours. Il


complète une présentation de Java donnée dans
un autre support
• Présente des caractéristiques avancées et
spécifiques de Java
• Rappel: toute l’information sur Java existe dans la
doc en ligne grâce à Javadoc : les commentaires
des sources deviennent de la documentation
Généralités
Java est Interprété

• Le code source est traduit par ‘javac’ en


byte-code indépendant de la machine
• Le byte code est exécuté (émulé) par
‘java’ (.class) ou ‘javaw’ (.jar)
• L’architecture est neutre: il suffit d'une
machine virtuelle Java sur la machine
d’exécution
• Apparence des applications préservée sur
toutes les plate formes.
Java est robuste

• plus simple que C++, très typé


• gestion très différente de la mémoire
(pointeurs, libération)
• nombreux contrôles de débordement,
utilisation systématique des exceptions
dans les bibliothèques d'outils
Java est Sécurisé

• Verifier : vérifie le byte code. (Chaque


fichier ".class" possède une marque
calculée, permettant d'interdire les
modifications triviales : insertion de virus
par simple ajout par exemple)
• Class Loader : responsable du
chargement des classes.
• Security Manager : accès aux ressources
Java est un langage
«!hautes!» performances

• en fonctionnalités, mais pas en vitesse


d’exécution
• les performances sont restaurées par la
compilation "Just In Time", qui permet à
la machine virtuelle de mémoriser, puis
ré exécuter le code natif correspondant à
une portion de boucle
• «!multithreaded!» (éventuellement sur
une machine mono-processeur)
Java est Moderne

• dynamique
• adapté à un environnement évolutif
• interprété
• lié aux design patterns par essence:
• itérateurs, objets fonction, inversion de
contrôle etc...
Performance

• Les JVM sont plus lentes que du code natif mais:


– compilation "just in time": une boucle déroulée
10000 fois n'est interprétée qu'une seule fois
• Java est gourmand en mémoire mais
– les options de lancement "java -X..." permettent
de contrôler les tailles des différentes piles et
tas (max heap, start heap, stack size):
– java -Xmx32m -Xms16m -Xss16m
De C++ à Java en
quelques différences
• Pas de structures ni d’unions
• Pas d'alias de types (typedef)
• Pas de préprocesseur (mais on peut
l'utiliser)
• Pas de variables ni de fonctions en dehors
des classes
• Pas d'héritage multiple de classes
• Pas de surcharge d’opérateur
• Pas de passage par copie pour les objets
De C++ à Java en
quelques différences
• Les varargs sont mieux faits
• Les templates s'appellent generics, et sont
différents
• Java propose une syntaxe avancée pour la
boucle for
• Java permet la conversion automatique
des types de base vers des objets
(wrapping ou boxing/unboxing)
• Les enums sont typés
• Java permet d'attacher au code des
annotations
Exceptions
Java
Principe
• Le programme qui détecte une erreur ne
peut pas présager de la manière dont
cette erreur pourra être traitée dans le
programme appelant (commentaire
français/anglais/ignoré/...)
• Si une erreur interdit de poursuivre
l’exécution d’un programme, il est inutile
d’obliger le programmeur à prévoir sa
terminaison harmonieuse
Mecanisme
• Les exceptions fournissent un flux de
contrôle distinct de celui offert par la
terminaison des fonctions
• C’est en quelque sorte un ‘abort’ de
l’exécution en cours, qui peut être
intercepté plus haut dans l’arbre d’appel
• Le programme qui lance l’exception
ignore ce qu’en fait le programme
appelant
Retour Sans Terminaison
des Fonctions

...
Déclaration

• Les exceptions dérivent de la classe


Exception, plus rarement des classes
Throwable ou Error.
• Une méthode doit spécifier ses exceptions
dans son entête
• public void ma_methode (….) throws
my_exception1, IOException { … }
Hiérarchie

Throwable

Error Exception

AssertionError

vos exceptions ...


API de
Throwable

• fillInStackTrace,
• getLocalizedMessage,
• getMessage,
• getStackTrace,
• initCause, getCause,
• setStackTrace, printStackTrace
Interception
• Après un bloc try, avec passage ou non
dans le premier gestionnaire catch
compatible avec le type de l’exception,
exécution de la clause finally


try
{ … }
catch (type-exception1 v1)
{ … }
catch (type-exception2 v2)
{ … }

finally
{
// on passe nécessairement par ici
...
}
Lancement d'exception

• Une exception doit toujours être allouée


par new

• Une exception est lancée avec "throw"

void foo() throws MonException {


...
if(...)throw new MonException(...);
...
}
catch (...) ?

• En Java, toutes les exceptions dérivent de


Exception, et tout ce qui peut être "lancé" dérive
de Throwable (Error + Exception)
• catch(Exception e){} intercepte toutes les
exceptions
• Noter que l'on n'est pas supposé écrire catch
(Throwable t){}, car on laisse normalement la
machine virtuelle gérer les erreurs
(AssertionError, ou échec de lancement de
Thread par exemple)
Exceptions Prédéfinies

• Le noyau et les bibliothèques Java


utilisent intensivement les exceptions
• ArrayIndexOutOfBoundsException
• BadStringOperationException
• IOException
• NoSuchMethodException
• ...
Obligation de
Spécification

• Toute fonction susceptible d’être


traversée par une exception doit le
spécifier dans sa clause “throws”
• Ce n’est pas le cas pour les exceptions
sous classes de RuntimeException
• A n’utiliser que si une exception n’est pas
susceptible d’être interceptée par le client,
et si ce n’est pas une assertion.
Assertions
Assert, et les Invariants

• La programmation, et la conception par


contrat, reposent sur l’utilisation
d’invariants.
• Un invariant est une propriété logique
toujours vraie en un point donné de
l’exécution d’un programme
• Un invariant n’est pas “testé” par un
programme, il est “validé” par “assert”
utilisation de "assert"
• L'instruction "assert" permet la
spécification contrôlée d'invariants
• On ne doit pas utiliser de tests
(if ...then...else) pour contrôler les
invariants mais toujours utiliser "assert"
• Cela sépare deux notions distinctes, et
améliore la lisibilité des programmes.
"assert" permet que la spécification d'un
programme soit testée à l'exécution.
Ou? Quand?
• Les invariants sont décrits par le
programmeur avant même de
programmer
• Ils restent dans le code pendant toute sa
durée de vie
• Ils sont testés de manière invisible
pendant le développement (“debug”)
• Ils sont ignorés en production
assert en Java
• Pour évaluer les assertions, il faut
exécuter avec l'option "-ea" ("enable
assertions")
• java -ea ...
• Pour lancer une assertion,
• assert expr_1 [: expr_str]
• "assert" lance un “AssertionError” si son
expr_1 est fausse, et la trace appelle la
méthode "toString" de extr_str.
Niveau de contrôle

• Le test d'assertions peut être contrôlé à trois


niveaux : global, package et classe. Ces fonctions
sont dans l’API de ClassLoader.
• public void setDefaultAssertionStatus(boolean
enabled);
• public synchronized void
setPackageAssertionStatus(String packageName,
boolean enabled);
• public void setClassAssertionStatus(String
className, boolean enabled);
• public void clearAssertionStatus();
• public boolean desiredAssertionStatus();
Types d’Invariants

• Invariants de classe
• Pré conditions
• Post conditions
• Invariants d’étape d’algorithme
• Variants de boucle
• Invariants de configuration,
d’architecture etc...
Invariants de Classe
Class Stack {
int arraySize;
int top;
boolean invariant(){
assert top>=0;
assert top<=size;
return true;
}
void push(int i){
assert invariant() ;
... // le code
assert invariant() ;
}
}
Interface “Checked"

• On peut Imposer la définition d’une fonction de test des


invariants de classe.
public interface Checked {
boolean invariant();
}

public class MaClasse implements Checked{


boolean invariant(){
...
return true;
}
Préconditions

Class Stack {
int arraySize;
int top;
boolean invariant(){...}
void getValueAtPos(int i){
assert invariant();
assert i>=0; //séparés exprès
assert i<top; //séparés exprès
... // le code
}
}
Post conditions / Delta
Conditions
Class Stack {
int arraySize;
int top;
boolean invariant(){...}
void push(int i){
assert invariant();
int auxTop=top;
... // le code
assert top=auxTop+1;
assert invariant();
}
}
Invariants d’étape
d’Algorithme

void f(int i){


//on cherche dans une liste
//une donnée qui s’y trouve
Link p=...;
while (p.data!=i)){p=p.next;}

assert p!=null; //bingo


p.doSomething();//on peut le faire
}
Variants de Boucle

int lastLoopVar=10000;
int loopVar=20;
while (1){
assert lastLoopVar>loopVar;
if(loopVar==0)break;
// (garantit la terminaison)
...
lastLoopVar=loopVar--;
}!
Processus de
développement
L’écriture d’un programme devrait se faire
selon le processus suivant:
1) définition des classes et interfaces de
programmation
2) écriture d’un programme de test
3) définition des structures de données
4) écriture des invariants, pré, post conditions
5) codage
Quelques Avantages de
l’Utilisation d’Assert
• Les invariants constituent une documentation
toujours exacte
• On sépare le contenu algorithmique de la
vérification de ses conditions d’utilisation
• On se prémunit d’avance contre les bugs de
régression, lors de modifications d’une classe
• On détecte les conditions logiques des erreurs
au plus tôt, alors que leurs conséquences
peuvent se manifester en des lieux inopportuns.
Introspection
Les classes
• La classe est l’élément de base de Java.
Tout est classe sauf les types primitifs
(int, float, double, ...) et les tableaux
• Il n’y a pas de fonctions, seulement des
méthodes
• Toutes les classes dérivent d'une
superclasse commune java.lang.Object
• “Object” fournit un grand nombre de
services
La classe Object
API fondamentale
• protected Object clone()
– crée et retourne une copie de l'objet. Remplace l’opérateur d’affectation copie (=)

• boolean equals(Object obj)


– permet de comparer deux objets par le contenu (requis pour les tables de hash code).

• protected void finalize()


– appelé par le garbage collector avant la récupération de l'espace occupé par l'objet.

• Class getClass()
– renvoie la classe d'un objet

• int hashCode() !
– renvoie la clé de hash code associée à l'objet

• String toString()
– retourne une chaîne de caractère. Utilisée par la jvm pour toutes les conversions en
chaînes
La classe Object
API - Threads

• void notify() "


– réveille un thread bloqué sur cet objet.
• void notifyAll()
– réveille tous les threads en attente pour cet objet.
• void wait()
– bloque un thread sur cet objet, en attendant qu'un
autre appelle notify()
La classe "Class"

• public final class Class extends Object


implements Serializable
• Les classes sont construites par la JVM
automatiquement, et sur appel de
defineClass dans un class loader.
• void printClassName(Object boo) {
• System.out.println("La classe de " +
boo + " est " + boo.getClass().getName());
Afficher un nom de
classe
void printClassName(Object o) {
System.out.println("La classe de " + o +
" est " + o.getClass().getName());
}

• La formulation suivante s'applique aussi


à un type de base

System.out.println("Le nom de classe de o


est : "+ o.class.getName());
Class et Paramétrage

• Charger dynamiquement une classe en


Java rend possible tout J2EE
• Un fichier de configuration (xml) fournit l
nom d’une classe inconnue du
framework, le framework peut en créer
des instances, et utiliser leur API
• Difficile à réaliser en C++
Class et Génération de
code à la volée
• Un programme Java peut
• générer le source d'une classe,
• invoquer le compilateur sur ce source,
• charger dynamiquement la classe avec la
fonction "forName"
• pour enfin en créer des instances et utiliser
leur API
• (Ce n'est possible en C++ qu'au prix de grandes
difficultés)
Méthodes statiques de
Class
static Class forName(String className)
– renvoie l'objet Class d'un nom donné
– Class t =
Class.forName("java.lang.Thread")

static Class forName(String name, boolean


initialize, ClassLoader loader)
– pareil, mais en utilisant un class loader particulier, avec
l'option d'initialiser ou non la classe désignée
API de Class
Nom, Héritage
• String getName()
– le nom

• Package getPackage()
– le package

• int getModifiers()
– un int contenant des booléens pour public, static etc...

• Class getSuperclass()

– la superclasse

• Class[] getInterfaces()
– retourne les interfaces implantées par la classe
API de Class
Classes Imbriquées

• Class[] getClasses()
– tableau de toutes les classes et interfaces publiques membres de cette classe

• Class[] getDeclaredClasses()
– tableau de toutes les classes et interfaces membres.

• Class getDeclaringClass()
– retourne la classe dont on est membre (éventuellement)
API de Class
Informations de Type
• boolean isAssignableFrom(Class cls)
– est ce que cls est une sous classe?

• boolean isInstance(Object obj)


– est ce que obj est une instance de cette classe

• boolean isInterface()
– interface?.

• boolean isPrimitive()
– type primitif? (int, void, etc...)

• boolean isArray()
– un tableau?

• Class getComponentType()
– le type des composants d'un "array".
API de Class
Accès aux constructeurs

• Constructor getConstructor(Class[] parameterTypes)

• Constructor[] getConstructors()

• Constructor getDeclaredConstructor(Class[] parameterTypes)

• Constructor[] getDeclaredConstructors()
– retournent les constructeurs publics, ou déclarés

• Object newInstance()
– crée une nouvelle instance avec le constructeur sans arguments
Accès aux membres
• Field getDeclaredField(String name)

• Field[] getDeclaredFields()

• Field getField(String name)

• Field[] getFields()
– retournent les champs de la classe publics ou non

• Method getDeclaredMethod(String name, Class[] parameterTypes)

• Method[] getDeclaredMethods()

• Method getMethod(String name, Class[] parameterTypes)

• Method[] getMethods()
– retournent les méthodes
Sécurité, ressources
• ClassLoader getClassLoader()
– le chargeur de cette classe.

• ProtectionDomain getProtectionDomain()
– les permissions accordées à cette classe

• URL getResource(String name)


– trouve une ressource de nom donné (les ressources sont tous les fichiers de
paramètres, ou de données accessibles dans le classPath - ou un jar).

• InputStream getResourceAsStream(String name)


– renvoie directement un flux d'entrée sur la ressource, prêt à en lire le contenu.

• Object[] getSigners()
– renvoie les "signers" de la classe - certificats d'authenticité-.

• boolean desiredAssertionStatus()
– renvoie le statut par défaut pour assert de la classe (les assertions peuvent
être contrôlées sur une base "classe par classe")
Contructeurs, champs,
méthodes
• Les classes Constructor, Field et Method offrent
une API permettant d’analyser un constructeur, un
champ ou une méthode.
• Types des paramètres d’une fonction
• Type de la valeur de retour
• Type d’un attribut
• Appel de la méthode ou avec des paramètres
appropriés
• Création d’une instance avec des paramètres
appropriés
Pointeurs de
Fonctions
Et classes anonymes
Pointeurs de fonctions
• Java ne permet pas de manipuler
explicitement de pointeur de fonction.
• Le seul moyen d’utiliser ces pointeurs se
fait via l’appel de méthodes, qui sont
toujours virtuelles en Java.
class Demon {
public abstract void
execute(/*params*/);
}
Pointeurs de fonctions

class MonDemon implements Demon{...


}
class MaClasse {
Demon undemon;
Ailleurs(Demon d ){undemon=d;}
void executeDemon(){
undemon.execute(/*params*/);}
}
Classes anonymes
Java permet de déclarer des classes "au vol", anonymes.

abstract class A {
abstract void g();
}
class Z {
void h(A a){ ... a.g(); ...}
}
...
{
Z z=new Z();
z.h(new A(){ void g(){ ... }});
}
Threads
Section Minimale d’introduction aux API
des Threads en vue du cours d’IHM
Les threads

• Petits morceaux de programme «!légers!»


faits pour fonctionner «!en parallèle!» en
partageant les données du même
processus
• écriture d’un texte
• bande son
• animation d’une image
Etats des threads

• Un thread peut être dans quatre états


distincts
• créé
• actif
• endormi
• tué
Option 1: Etendre Thread

class PrimeThread extends Thread {


long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;}
public void run() {/*calcule les
nombres premiers supérieurs à minPrime*/}
}
//////// pour lancer:
PrimeThread p = new PrimeThread(143);
p.start();
Option 2:
Implanter Runnable
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {this.minPrime = minPrime;}
public void run() {/*calcule les nombres premiers
supérieurs à minPrime*/ }
}
//////// pour lancer:
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
API de contrôle de
thread
void start()
lance le thread. la machine virtuelle java appelle run()
void run()
void interrupt();
void destroy()
void setDaemon(boolean on)
void join([long millis [, int nanos]])
attend la terminaison de ce thread
API Statique de thread
static Thread currentThread() //le thread courant
static int activeCount() //threads actifs dans le groupe
static boolean holdsLock(Object obj) // le thread courant a
t'il un lock sur l'objet?
static boolean interrupted()
static void sleep(long millis[, int nanos])
static void yield() //arrête temporairement
static int enumerate(Thread[] tarray) //les threads actifs
du groupe
static void dumpStack()
API de thread:
Accesseurs
ClassLoader getContextClassLoader()

String getName(); void setName(String name)

int getPriority(); void setPriority(int newPriority);

ThreadGroup getThreadGroup();

boolean isAlive(); boolean isDaemon() ; boolean


isInterrupted()

void setContextClassLoader(ClassLoader cl)

void checkAccess() throws SecurityException

possibilité de modifier ce thread?


Classes Génériques
Les classes Génériques

• Java fournit un mécanisme permettant de


paramétrer des classes, fonctions et
attributs par des types
• Ces constructions sont très utiles pour
permettre un contrôle à la compilation
(typage fort) des données passées à des
programmes
• Cela permet par exemple de créer des
conteneurs “homogènes”
Classes Génériques
http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf
• Un programme sans génériques

• Un programme avec génériques


Déclaration d'interfaces
génériques
Sémantique des
génériques
• Très différente de C++ qui génère une nouvelle
classe pour chaque spécialisation d'un template
• Ici, le compilateur génère une seule
représentation de la classe générique, une fois
pour toutes. L'information de type explicite ou
implicite (passage d'un paramètre de type connu)
lui permet de générer automatiquement les casts.
• Le code Java produit par des génériques reste
compatible avec du code java standard
Génériques et
Sous typage

• Le comportement du compilateur pour le sous typage


peut paraître contre intuitif:
List<String> ls = new ArrayList<String>(); //ok

List<Object> lo = ls; //invalide

• C'est normal si l'on considère cet exemple:


lo.add(new Object()); // ok

String s = ls.get(0); // invalide: on a un


"Object" à la place d'une "String"
Le type "Wildcard"
• Comment faire alors pour déclarer une fonction
qui prendrait en paramètre un conteneur de
n'importe quoi?

• Mauvaise idée:
Le type "Wildcard"

• La solution: utiliser le "Wildcard"


Désigner une sous
hiérarchie

• Considérons une méthode devant dessiner une


liste de formes (naïvement List<Shape>).

• Le compilateur refusera un appel avec


List<Circle>, alors que c'est raisonnable
Désigner une sous
hiérarchie

• Il faut utiliser:
public void drawAll(
List<? extends Shape> shapes)
{ ... }
Méthodes Génériques

• On peut également paramétrer des méthodes par


un type.
• On utilise ce mécanisme normalement quand il
existe une dépendance
– entre les types d'un ou plusieurs paramètres
– et/ou celui de la valeur de retour.
Méthodes Génériques
• Exemple: ranger les éléments d'un tableau dans
une collection. Ne compile pas:

• Il faut écrire:
Wildcard OU Générique ?

• En cas d'ambiguïté, s'il n'y a pas de dépendance


entre types, on préfère le "Wildcard"

• VS:
Wildcard ET générique?

• Voici la déclaration de la fonction copy des


collections.
• Elle copie une liste de T ou de sous types (src)
vers une liste de T
Génériques imbriqués

• Bien sûr, on peut utiliser un type générique


comme paramètre d'un autre.
• Exemple: la fonction "drawAll" conserve un
historique dans une liste statique:
Super
• <? extends T> signifie désigne un sous type de T
• <? super T> désigne un supertype de T, s’il est
utilisé pour l’initialisation du générique.
• List<?!super!String>!st;
• st=new!ArrayList<String>();!!//ok
• st=new!ArrayList<Object>();!!//ok
• st=new!ArrayList<Serializable>();!!//!String!impl
émente!Serializable!!!
Pour comprendre les
Génériques

• Tout repose sur ce que le compilateur


connaît.
• List<?!super!String>!st; dit que tout sous
type de String sera compatible pour
l’insertion. Pas plus.
• Une fois que le code compile, les
paramètres génériques sont oubliés (et
inaccessibles - pas d’introspection)
Présentation de l’API
Math
• C’est la bibliothèque mathématique, elle définit
deux constantes E et PI (représentés sous forme
de double)
• Toutes ses méthodes sont statiques et retournent
des ‘double’ (sauf rint et round):
• sin, cos, tan, asin, acos, atan, exp et log, sqrt et
cbrt, élévation à une puissance pow, abs
• approximations numériques entières : ceil, floor,
rint, round
• nombre aléatoire random(dans [0, 1])
• fonctions de comparaison min et max
• fonctions hyperboliques sinh, cosh et tanh
• ...
StrictMath, BigInteger

• La classe StrictMath garantit les résultats de


certains algorithmes numériques de références
mais dans certains cas cela peut conduire à une
perte d’efficacité
• java.math offre deux classes
• BigInteger et
• BigDecimal
• permettant de manipuler des nombres avec une
grande précision …
La classe File
• Gestion et stockage des fichiers (création,
suppression, position, etc.) par le biais d’une
représentation abstraite des fichiers et
répertoires
• File(String) crée la représentation abstraite d’un
fichier ou répertoire
• isFile()
• isDirectory()
• listFiles()
Collections

• java.util fournit des classes de collections


de données
• Ces classes fournissent des algorithmes
nombreux et efficaces
• Elles respectent les concepts sous-jacents
aux catégories auxquelles elles
appartiennent (accès lecture en O(1) ou
O(n) par exemple)
Avantages des
Collections

• Réduction des efforts de programmation grâce à


l’apport de structures de données et d’algorithmes
riches et souples
• Fiabilité et performances des programmes grâce à
la qualité des implémentations fournies pour ces
structures de données et ces algorithmes
• Amélioration de la réutilisation des programmes
qui s’appuient sur ces collections standards
Catégories de
Collections

Il existe trois grandes catégorie de classes de ce


type :
• les séquences (chaque élément à un rang)
• les ensembles (pas de répétition, pas de rang)
• les tables associatives (couples <clé, valeur>)
Séquences
AbstractList<E>

AbstractSequentialList<E> Vector<E>

ArrayList<E>

LinkedList<E> Stack<E>

ArrayList est rapide en accès aléatoire, mauvais en insertion


LinkedList est mauvais en accès aléatoire, rapide en insertion
Vector offre des services complémentaires (redimensionnement)
Ensembles
AbstractSet<E>

HashSet<E> TreeSet<E>

LinkedHashSet<E>

HashSet ne garantit pas l’ordre des éléments


LinkedHashSet utilise une liste chainée
supplémentaire et préserve l’ordre d’insertion
TreeSet est un arbre binaire de recherche
Tables Associatives
AbstractMap<E>

HashMap<E> TreeMap<E>

WeakHashMapMap<E>

LinkedHashMap<E>
IdentityHashMap<E>

IdentityHashmap compare des références (==)


WeakHashMap utilise des références weak: pas de
fuites mémoire
Itérateurs

• Un Iterator<E> permet de parcourir une collection


(séquence ou ensemble)
• Il se positionne sur le premier élément de la collection et en
donne successivement chaque élément: ArrayList<E>,
LinkedList<E>, TreeSet<E>, HashSet<E>, etc.
• Pour les ensembles, l’ordre d’itération est arbitraire.
• Les tables associatives ne disposent pas d’itérateurs. Pour
parcourir la collection des clés (ou des valeurs), il faut
passer par des méthodes intermédiaires qui récupéreront
ces collections.
Itérateurs: API
• Les deux méthodes de base de Iterator<E> sont hasNext()
et next()
• remove() supprime l’élément renvoyé par le dernier
appel à next() (appel de next() obligatoire avant d’appeler
remove())
• Un itérateur est toujours créé par la collection
LinkedList<E> l;
Iterator<E> i=l.listIterator();
while(i.hasNext()){
E e= i.next();
// faire quelque chose avec e
}
API Commune aux
Séquences et Ensembles

• boolean add(E e)
• boolean remove(E e)
• boolean contains(Object o)
• boolean isEmpty()
• int size()
Api des Séquences

• E get (int i)
• E set (int i, E e)
• void add (int i, E e)
• E remove (int i)
Classe Stack<E>
• E push(E e) // empile e en sommet de pile
• E pop() // dépile le sommet de pile
API des Ensembles

• Les méthodes générales s’appliquent aux ensembles


tout en garantissant l’unicité des éléments. La classe
TreeSet<E> propose en plus:
• SortedSet<E> headSet(E e) // éléments < e
• SortedSet<E> tailSet(E e) // éléments >= à e
• SortedSet<E> subSet(E e1, E e2) // éléments
compris entre e1(inclus) et e2(exclus)
API des Hash Tables
• V get(Object k)
• V put(K k, V v) // associe v à la clé k et renvoie
l’ancienne valeur (ou null)
• V remove(Object k) // supprime la clé k et
renvoie l’ancienne valeur (ou null)
• boolean containsKey(Object k)
• Set<K> keySet() // l’ensemble des clés
• Collection<V> values() // renvoie la collection
des valeurs
API Complémentaire de
ListIterator
ListIterator<E>est un itérateur bidirectionnel
• hasPrevious() et previous()
• previousIndex() et nextIndex() (renvoient les index
encadrant l’élément courant).
• set(E) remplace l’élément courant
• add(E) permet d’ajouter juste après previous, et
avant next (appel de next non affecté, appel de
previous changé, nextIndex et previousIndex
augmentés de 1)
Exemple
LinkedList<Integer> l = new LinkedList<Integer>(); …
ListIterator<Integer> parcours = l.listIterator();
// listIterator méthode de AbstractList<E>
// parcours.next(); //exception, la liste est vide
parcours.add(8);
parcours.add(7); // ajoute 7 en deuxième position
parcours.add(6); // ajoute 6 en troisième position
parcours.previous();
parcours.add(5); // ajoute 5 en troisième position
• listIterator(int) positionne l’itérateur à la valeur donnée
en argument
• set(E) remplace l’élément renvoyé par le dernier appel à
next() ou par l’élément donné en argument
La classe Collections
• Offre des algorithmes sous forme de méthodes
statiques
• void reverse(List<?> l) // inverse l
• void shuffle(List<?> l) // permute aléatoirement l
• <T> int binarySearch(List<? extends
Comparable<? super T>> l, T k)
• (cherche k dans la séquence triée l en utilisant un
algorithme dichotomique, renvoie la position de k
dans la séquence
Autres Fonctions de
Collections
• void swap(List<?> l, int i, int j) // échange les valeurs qui
se trouvent en positions i et j dans l
• <T extends Object & Comparable<? super T>> T
max(Collection<? extends T> c) // max de la collection c
(idem pour min) selon l’ordre naturel
• <T> boolean replaceAll (List<T> l, T v1, T v2) // remplace
tous les éléments v1 par v2 dans la séquence l
• <T extends Comparable<? super T>> void sort(List<T>
l) // trie la séquence en utilisant un algorithme de tri par
fusion
Gestion de la Mémoire
Pas de destructeur,
mais “finalize”
• La libération de la mémoire est automatique et
désynchronisée
• Le «!garbage collector!» ne peut libérer que la mémoire
allouée par Java, et seulement si toutes ses strong références
ont disparu
• finalize est appelée par le GC avant destruction d’un objet
• permet la libération de toutes les autres ressources
• permet la "résurrection de l'objet" en créant une référence
• On doit appeler la méthode finalize de sa super classe
" void finalize () {
super.finalize () ; ….
}
Gestion mémoire
• Allocation: new: retourne une "strong reference"
• Libération automatique gérée par un processus
appelé garbage collector qui détecte les objets sans
référence
• Ce processus fonctionne en tâche de fond de faible
priorité, dans un thread parallèle, mais peut être
déclenché explicitement (méthode System.gc () )
– ne peut libérer que la mémoire allouée par Java
– ne peut pas tout libérer malgré tout
Types de Référence
• Trois autres types de références, en plus de la
référence "strong"
– weakReference (n'empêche pas le gc s'il n'y a
plus de strong ref)
– softReference (comme weak, mais l'objet
survit tant que la mémoire le permet)
• utile pour mettre en œuvre des caches
– phantomReference (permet de savoir quand
un objet va être détruit). C'est une alternative
saine à la redéfinition de "finalize"
Comment on les utilise

• Un objet est “strong” s’il est accessible, sans


devoir passer par une référence
• Un objet est “soft” s’il n’est plus strong, mais
qu’il existe une référence “soft”
• Un objet est “weak” s’il n’est pas “soft”, mais
qu’il existe une référence “weak”
• Un objet est “phantom” s’il n’est pas “ weak”,
mais qu’il existe une référence “ phantom”
Queues de Références

• Le constructeur d’une référence permet de passer


une ReferenceQueue en paramètre.
• Lorsque le GC constatera le changement de statut
d’un objet, il placera la référence correspondante
dans la queue déclarée.
• Le programme “client” visite ses queues à
intervalles réguliers, et prend les décisions
appropriées
java.lang.ref
Reference<T>

• void clear() // détruit le lien !


• boolean enqueue() // place dans la queue !
• T get() // retourne le référent (ou null si clear)
• boolean isEnqueued()
java.lang.ref
PhantomReference<T>

PhantomReference(T!referent,
ReferenceQueue<? super T>!q)
• Crée une nouvelle référence fantôme sur
“referent”)
• La queue est obligatoire
• La fonction “get” renvoie toujours “null”
SoftReference<T>
WeakReference<T>

• SoftReference(T!referent)
• SoftReference(T!referent,
ReferenceQueue<? super T>!q)

• WeakReference(T!referent)
• WeakReference(T!referent,
ReferenceQueue<? super T>!q)
java.lang.ref
ReferenceQueue<T>

• ReferenceQueue()
• Reference <? extends T> poll()
• Reference<? extends T> remove()
• Reference<? extends T> remove (long!t)
• Removes the next reference object in this
queue, blocking until either one becomes
available or the given timeout period
expires.
Listing C:
MyReference.java

import java.lang.ref.*;

public class MyReference extends SoftReference {


! public MyReference( Object referent ) {
!!! super( referent );
! }

! public MyReference( Object referent, ReferenceQueue q ) {


!!! super( referent, q );
! }

public String toString() {


!!! return String.valueOf(get());
! }
}
Listing B:
MemoryBlock.java

public class MemoryBlock {


!int id;
!int size;
! byte[] block;

! public MemoryBlock( int id, int size ) {


!!!this.id = id;
!!!this.size = size;
!!! block = new byte[size];
!!!System.out.println( "MemoryBlock created: "+this );
! }

! public String toString() {


!!! return "{id="+id+",size="+size+"}";
! }

! protected void finalize() {


!!!System.out.println( "MemoryBlock finalized: "+this );
! }
}
Listing A:
ReferenceQueue example

import java.lang.ref.*;
import java.util.*;

public class MemoryTest3 {


! public static void main( String[] args ) {
!!!ReferenceQueue queue = new ReferenceQueue();
!!!ArrayList blocks = new ArrayList();
!!!int size = 65536;
!!! for ( int id=0; true; id++ ) {
!!!!!blocks.add( new MyReference(new MemoryBlock(id,size),queue) );
!!!!! while ( true ) {
!!!!!!! Reference ref = queue.poll();
!!!!!!! if ( ref == null )
!!!!!!!!! break;
!!!!!!!blocks.remove( ref );
!!!!! }
!!!!!System.out.println( "blocks: "+blocks );
!!!!! size *= 2;
!!! }
! }
}

http://articles.techrepublic.com.com/5100-10878_11-1049546.html#
Listing D:
WebCache.java
import java.io.*;
import java.net.*;
import java.util.*;

public class WebCache {


! WeakHashMap cache = new WeakHashMap();

! public class WebObject {


!!! public String type;
!!! public byte[] content;
! }

! public WebObject get( String spec )


!!! throws MalformedURLException,IOException {
!!! URL url = new URL(spec);
!!! WebObject obj = (WebObject) cache.get(url);
!!! if ( obj == null ) {
!!!!! URLConnectionconn = url.openConnection();
!!!!! obj = new WebObject();
!!!!! obj.type = conn.getContentType();
!!!!! obj.content = new byte[conn.getContentLength()];
!!!!! conn.getInputStream().read( obj.content );
!!!!! cache.put( url, obj );
!!! }
!!! return obj;
! }
}
Weak References
• Illustration du problème des références. Cette
structure de données associe une information à un
Socket, au caractère volatile.
• Que se passe t'il quand un socket disparaît?
• (d'après http://www-128.ibm.com/developerworks/java/library/j-jtp11225/)
Fuite de mémoire

• (d'après http://www-128.ibm.com/developerworks/java/library/j-
jtp11225/)
Comportement du GC et
utilisation de mémoire

• (d'après http://www-128.ibm.com/developerworks/java/library/j-
jtp11225/)
Exemple de source

• Solution: utiliser WeakHashMap à la place de


HashMap: intuition du code
• (d'après http://www-128.ibm.com/developerworks/java/library/j-
jtp11225/)
Annotations
Annotations
• Les annotations constituent un mécanisme
puissant par lequel un source peut se voir attacher
des informations exploitées par le compilateur ou
par des outils annexes capables de générer du
code auxiliaire.

• L'intérêt est de permettre au développeur de ne


maintenir qu'un seul source.
Définition d'annotations

• Les annotations sont typées, en utilisant une


variante du mot clef “interface”: “@interface”.
• Exemple de définition:
public @interface MyAnnotation {
String doSomething();
}
• Exemple d'utilisation:
MyAnnotation (doSomething="oh")
public void mymethod() { .... }
Trois Catégories
d'annotations

Les annotations appartiennent à trois catégories, selon le


nombre de leurs fonctions membres:
• Marker: pas de donnée – un type seul - un booléen
• Single Element : un seul attribut
• Multi Value : plusieurs attributs
// Marker
public @interface MyAnnotation {}
@MyAnnotation public void mymethod() {....}
Exemple Single Valued

public @interface MyAnnotation {


String doSomething();
}
// Pas besoin de nommer le paramètre
@MyAnnotation ("What to do")
public void mymethod() { .... }
Exemple Multi Valued

• public @interface MyAnnotation {


• String doSomething();
• int count();
• String date();
• }
• // Utilisation :
• @MyAnnotation (doSomething="What to do",
count=1, date="09-09-2005")
• public void mymethod() { .... }
Restrictions

• Les méthodes ne doivent pas avoir de paramètres


• Pas de clauses de lancement d'exceptions non plus
• Les types de retour ne peuvent appartenir qu'à la
liste suivante:
• types primitifs (int, float...)
• String
• Class
• enum
• ou des tableaux des types ci dessus
Annotations prédéfinies

• Annotations simples:
– Override
– Deprecated
– SuppressWarnings
• Méta annotations
– Target
– Retention
– Documented
– Inherited
Override

• public class Test {


• @Override public String toString() {
• return super.toString() + "Override"; }
• }

• Si le nom de la fonction (toString) ne correspond


pas à une méthode de la super classe, le
compilateur génère une erreur
Deprecated et
SuppressWarnings

• Deprecated permet de générer des avertissements


lors de la compilation, si un programme utilise
une fonction dépréciée

• SuppressWarnings permet de désactiver les


avertissements du compilateur
La Méta annotation
«!Target!»
• Target est une annotation d'annotation qui permet
de spécifier ses emplois possibles.
• @Target(ElementType.TYPE)—tout élément
• @Target(ElementType.FIELD)—donnée
• @Target(ElementType.METHOD)
• @Target(ElementType.PARAMETER)
• @Target(ElementType.CONSTRUCTOR)
• @Target(ElementType.LOCAL_VARIABLE)
• @Target(ElementType.ANNOTATION_TYPE)
– Indique que le type déclaré est une méta
@Target(ElementType.AN
NOTATION_TYPE)

• @Target(ElementType.ANNOTATION_TYPE)
• Indique que le type déclaré est une méta annotation (donc
une annotation d’annotations. Par exemple:
@Target(ElementType.ANNOTATION_TYPE)
public @interface MetaAnnotationType
{ ... }
Utilisation des tableaux

• On peut grouper les paramètres des «!single!»


@Target({ElementType.FIELD,
ElementType.METHOD})
public @interface Ok { ... }
• Mais on ne peut pas les répéter:
@Target({ElementType.FIELD,
ElementType.FIELD})
public @interface Erreur { ... }
Exemple de Target

@Target(ElementType.METHOD)
public @interface Test_Target {
public String do();
}

• Utilisation correcte:

@Test_Target("glop")
public void methode1() {...}
Retention

• La méta annotation «!Retention!» indique la durée


de vie de l'annotation: source seul, classe, machine
virtuelle
• RetentionPolicy.SOURCE
– ignorées par le compilateur
• RetentionPolicy.CLASS
– utilisables par un outil qui accède aux .class
• RetentionPolicy.RUNTIME
– lisibles à l'exécution
Documented

• Indique que Javadoc doit documenter l'annotation


• @Documented
• public @interface Test_Documented {...}
• public class TestAnnotations {
• public static void main(String arg[]){}
• @Test_Documented(...) public void m(){}
• }
• //@Test_Documented sera dans la javadoc
Inherited

• Par défaut, les annotations d'une classe ne sont


pas héritées par les sous classes
• La méta annotation «!@Inherited!» permet de
modifier ce comportement sélectivement
• Ne s!'applique qu'aux annotations attachées aux
classes
• L'annotation peut être surchargée, le mécanisme
cherchant à partir de la classe courante
• Les annotations ne sont pas héritées par les classes
qui implémentent des interfaces
Utilisation à Run Time

• Pour être visible à l’exécution, une


annotation doit être méta annotée:
@Retention(RetentionPolicy.RUNTIME)

• L'interface AnnotatedElement définit les


méthodes pour le traitement des
annotations par introspection
AnnotatedElement

<T extends Annotation> getAnnotation(Class<T>)


Annotation[] getAnnotations()
Annotation[] getDeclaredAnnotations()

• (Sauf héritées).
boolean isAnnotationPresent(Class< ? extends
Annotation>)

• Particulièrement utile dans le traitement des


annotations de type marqueur.
Exemple Todo

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Todo {
String[] description();
}
Cette classe liste ses
annotations
import ...;
@Todo(description = {"Modifier cette classe",”avant l’hiver”})
public class TestAnnotation {
!!public static void main(String[] args) {
!!!!Todo todo = null;
!!
!!!// traitement annotation sur la classe
!!!!Class classe = this.class;
!!!!todo = classe.getAnnotation(Todo.class);
!!!!if (todo != null) {
!!!!!!System.out.println("classe "+ classe.getName());
!!!!!!for(String desc : todo.description()) {
!!!!!!!!System.out.println("!!!! _ "+desc);
!!!!!!}!!!!
}
} // d’après http://www.jmdoudoux.fr/java/dej/chap010.htm
annotations traitées à la
compilation
Java 6 intègre la déclaration de processeurs d’application au
compilateur javac
• -processor
• -proc
• -processorpath
• -A (options aux processeurs d'annotations cle=valeur)
• -XprintRounds
(informations sur le traitement des annotations)
• -XprintProcessorInfo
liste les annotations traitées
Passes (Rounds)

• Le traitement des annotations se fait en


plusieurs passes (round).
• A chaque passe le processeur est appelé pour
traiter des classes qui peuvent avoir été générées
lors de la précédente passe.
• Lors de la première passe, ce sont les classes
fournies initialement qui sont traitées.
Processeurs
d’annotations

• Un processeur implémente l'interface Processor.


• L'interface javax.annotation.processing Processor définit les
méthodes d'un processeur d'annotations.
• Pour définir un processeur, il est possible de créer une classe
qui implémente l'interface Processor mais le plus simple est
d'hériter de la classe abstraite
javax.annotation.processing.AbstractProcessor.
AbstractProcessor
• La classe AbstractProcessor contient une variable nommée
processingEnv de type ProcessingEnvironment.
• ProcessingEnvironment fournit les services de classes
utilitaires ou qui permettent des traitements avec
l'extérieur du processeur
• La méthode la plus importante est la méthode process()!:
c'est elle qui va contenir les traitements exécutés par le
processeur. Elle possède deux paramètres!:
• Un ensemble des annotations qui seront traitées par le
processeur
• Un objet qui encapsule l'étape courante des traitements
Api de
AbstractProcessor
• Iterable<? extends Completion>
getCompletions(Element element,
AnnotationMirror annotation,
ExecutableElement member, String userText)
retourne une liste vide itérable de
complétions
• Set<String> getSupportedAnnotationTypes()
(si le processeur est annoté avec
SupportedAnnotationTypes)
• Set<String> getSupportedOptions()
(si le processeur est annoté avec
SupportedOptions)
Api de
AbstractProcessor
• SourceVersion getSupportedSourceVersion()
(annoté avec SupportedSourceVersion)
• void init(ProcessingEnvironment processingEnv)
initialise le processeur
• protected boolean isInitialized()
• abstract boolean process(Set<? extends
TypeElement> annotations,
RoundEnvironment roundEnv)
Traite un jeu de types d’annotations sur
les éléments issus du round précédent. Si
vrai est renvoyé, les annotations ne seront
pas proposées aux autres processeurs.
ProcessingEnvironment
• (get)Filer!: permet la création de fichier
• (get) Messager!: permet d'envoyer des messages
affichés par le compilateur
• (get) Elements!: fournit des utilitaires pour les
éléments
• (get) Types!: fournit des utilitaires pour les types
• La méthode getRootElements() renvoie les classes
Java qui seront traitées par le processeur dans
cette passe.
Annotations des
Processeurs
Deux annotations sont dédiées aux processeurs
d'annotations et doivent être utilisées sur la classe
du processeur!!:
• @SupportedAnnotationTypes!: cette annotation
permet de préciser quelles seront les types
annotations traitées par le processeur. La valeur
«!*!» permet d'indiquer que tous les types seront
traités.
• @SupportedSourceVersion!: cette annotation
permet de préciser la version du code source traité
par le processeur
Exemple
import ...;
!
@SupportedAnnotationTypes(value = { "*" })
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class TodoProcessor extends AbstractProcessor {
@Override public boolean process(
Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
!Messager messager = processingEnv.getMessager();
!
for (TypeElement te : annotations) {
messager.printMessage(Kind.NOTE, "Traitement annotation "
" + te.getQualifiedName());
!
for (Element element : roundEnv.getElementsAnnotatedWith(te)) {
messager.printMessage(Kind.NOTE, " Traitement element "
" " + element.getSimpleName());
Todo todo = element.getAnnotation(Todo.class);
!
if (todo != null) {
messager.printMessage(Kind.NOTE, " affecte le " + todo.dateAssignation()
" " + " a " + todo.assigneA());
}
}
}
!
return true;
}
Conclusion
Pointeurs

• http://www.javasoft.com : Site officiel Java


– (JDK et documentation, téléchargement)
• http://www.javaworld.com : Info sur Java

• http://www-sor.inria.fr/~dedieu/java/
• Le langage Java, Touraivane, cours ESIL, Dpt GBMA
• Le polycopié de C. Chaouiya, cours ESIL, Dpt GBMA
• La première version de ce support de cours a été produite par
Marc Daniel, ESIL

• Google : http://www.google.fr/search?q=java

Vous aimerez peut-être aussi