2. La classe Class

2.1. Utilisation de Class

La classe Class est le point d'entrée de l'exploration du contenu d'une classe par introspection. On peut obtenir une instance de Class de trois façons :
  • en invoquant la méthode getClass() sur n'importe quel objet ;
  • en appelant cet objet directement, par exemple String.class ou Marin.class sont des objets instances de Class ;
  • en utilisant la méthode statique Class.forName(String), à laquelle on passe le nom complet d'une classe, avec le nom du package dans lequel elle se trouve.
Une classe est toujours chargée par un class loader en Java. Un class loader est une instance de la classe ClassLoader. On peut obtenir une référence sur le class loader qui a chargé une classe donnée en utilisant la méthode getClassLoader(). Certaines classes, chargées par un class loader particulier appelé bootstrap class loader ne retournent pas d'instance, mais null. C'est le cas notamment de toutes les classes des sous-packages de java.*.

2.2. Méthodes disponibles

La classe Class expose un jeu de méthodes qui permet d'otenir les constructeurs, les champs, et les méthodes de cette classe. Chacune de ces méthodes existe en quatre versions. Voyons-les pour l'accès aux champs d'une classe.
  • getFields() : retourne la liste des champs publics de cette classe, et de ses super-classes ;
  • getDeclaredFields() : retourne la liste des champs publics ou non, déclarés uniquement dans cette classe ;
  • getField(String name) : retourne le champ dont le nom est précisé, s'il est public, et qu'il soit dans cette classe ou dans une super-classe ;
  • getDeclaredField(String name) : retourne le champ dont le nom est précisé, qu'il soit public ou non, mais uniquement s'il se trouve dans cette classe.
Les méthodes susceptibles de retourner plusieurs champs les retournent dans un tableau. Si aucun champ ne correspond à ce qui est demandé, ce tableau est vide. Aucune méthode qui doit retourner un tableau ne retourne null dans cette API. En résumé, on peut demander la liste des champs, constructeurs ou méthodes, qui est retournée dans un tableau. On peut demander un membre particulier de cette liste, en précisant son nom, et éventuellement les éléments pris en paramètre (cas des constructeurs et des méthodes). Enfin, soit on choisit de faire cette interrogation sur cette classe et ses super-classes, auquel cas on peut obtenir des membres publics, soit on la fait uniquement sur cette classe, sur tous ses membres, publics ou non. Examinons maintenant toutes ces méthodes.
  • getAnnotations() et getAnnotation(Class<A> a) : retournent le tableau des annotations posées sur cette classe, ou l'annotation de la classe passée en paramètre, si elle existe.
  • getDeclaredAnnotations() : retourne le tableau de toutes les annotations de cette classes, éventuellement héritées des interfaces implémentées, ou des super-classes.
  • getConstructors() et getConstructor(Class<?>... types) : retournent le tableau des constructeurs publics de cette classe, ou le constructeur qui prend en paramètre la liste des classes (donc des types) indiquée.
  • getDeclaredConstructors() et getDeclaredConstructor(Class<?>... types) : retournent le tabeau des constructeurs de cette classes, publics ou non, ou le constructeur qui prend en paramètre la liste des classes (donc des types) indiquée.
  • getFields() et getField(String field) : retournent le tableau des champs publics de cette classe, ou le champ dont le nom est passé en paramètre. Les champs publics des super-classes sont présents dans la liste.
  • getDeclaredFields() et getDeclaredField(String field) : retournent le tableau des champs déclarés dans cette classes seulement, publics ou non.
  • getMethods() et getMethod(String, Class<?>... types) : retournent le tableau des méthodes publiques de cette classe, et de ses super-classes, ou la méthode qui correspond aux paramètres spécifiés.
  • getDeclaredMethods() et getDeclaredMethod(String, Class<?>... types) : retournent le tableau des méthodes de cette classe, publiques ou non, ou la méthode qui correspond aux paramètres précisés.
Les méthodes suivantes permettent d'obtenir des informations sur cette classe.
  • getCanonicalName() : retourne le nom complet de cette classe, s'il existe. Certaines classes n'ont pas de nom, notamment les classes anonymes (c'est d'ailleurs la raison pour laquelle on les appelle anonymes !).
  • getName() : retourne le nom du type de cette classe ( class, interface, type primitif ou tableau).
  • getSimpleName() : retourne le nom de cette classe, sans son nom de package.
  • getPackage() : retourne une instance de la classe Package, qui modèlise le package dans lequel se trouve cette classe.
  • getSuperClass() : retourne la super-classe de cette classe, sous forme d'une instance de Class.
  • getInterfaces() : retourne un tableau des interfaces implémentées par cette classe, sous forme d'instances de Class.

2.3. Remarque sur la propriété Accessible

Les classes Method, Field et Constructor étendent toutes les trois la classe AccessibleObject. Cette classe concrète expose notamment une propriété : accessible, de type booléen, en lecture et en écriture. Ce booléen permet d'activer ou non la vérification par la machine Java des droits d'accès sur l'objet considéré, de type Method, Field ou Constructor. Il est à false par défaut, ce qui signifie que la machine Java vérifie ces droits d'accès. On peut le mettre à true, ce qui a pour effet de supprimer cette vérification, et de rendre utilisables les membres privés d'une classe, de toute une application. Notons bien que positionner ce booléen à true pour un champ (par exemple), ne rend pas ce champ public s'il était private. On peut s'en rendre compte si l'on interroge la méthode getModifiers() de ce champ une fois la propriété accessible changée. Nous verrons un exemple d'application dans la suite.

2.4. Type d'une classe

La classe Class expose également un jeu de méthodes qui permettent de tester à quel type de classe nous avons affaire. Ce type peut être de différentes natures. Voyons ces méthodes.
  • isInterface() : retourne true si cette classe est une interface.
  • isEnum() : retourne true si cette classe est une énumération.
  • isLocalClass() et isMemberClass() : retourne true s'il s'agit d'une classe locale ou membre, respectivement.
  • isAnonymousClass() : retourne true si cette classe est anonyme.
  • isPrimitive() : retourne true si cette classe modélise un type primitif. Notons que tous les types primitifs sont associés à des classes : int.class, float.class, etc...
  • isArray() : retourne true si cette classe modélise un tableau.
  • isSynthetic() : retourne true si cette classe est une classe synthétique. Une classe synthétique est une classe créée dynamiquement, par le compilateur ou à l'exécution du code. Une classe synthétique est par exemple associée à chaque clause switch.

2.5. Création d'une instance à partir d'un objet Class

La classe Class expose une méthode newInstance() qui permet de créer une nouvelle instance de cette classe. Cette méthode invoque le constructeur vide de cette classe, qui doit donc exister. L'utilisation de cette méthode permet, simplement à partir du nom d'une classe sous forme d'une chaîne de caractères ( String), qui peut-être lue dans un fichier par exemple, de créer des instances de cette classe.

Exemple 47. Création d'un objet par introspection

// dans une méthode main
String className =  "org.paumard.model.Marin" ;

 // chargement d'une classe à partir de son nom
 // jette une exception du type ClassNotFoundException
Class<?> marinClass = Class.forName(className) ;

 // instanciation d'un objet à partir de sa classe
 // jette deux exceptions : IllegalAccessException, InstantiationException
 // l'objet o est en fait de type org.paumard.model.Marin
Object o = marinClass.newInstance() ;

Notons que l'on dispose également de deux méthodes pour tester si un objet possède le type modélisé par cette classe, ou si une classe est une extension, ou une implémentation d'une autre classe.
  • isInstance(Object o) : retourne true si cet objet est une instance de cette classe. Rappelons que cette classe peut modéliser une interface, ou une des super-classes de l'objet.
  • isAssignableFrom(Class<?> clazz) : retourne true si cette classe ou interface est une super-classe, une super-interface, ou une classe qui implémente, directement ou non, la classe passée en paramètre. Dans le cas de classes modélisant les types primitifs, cette méthode ne retourne true que si les deux types sont identiques. Effectivement, les classes qui modélisent les types primitifs sont particulières : elles n'étendent pas Object, et n'implémentent aucune interface.
La méthode isAssignableFrom(Class<?> signifie que l'on peut instancier cette classe en construisant une instance de la classe passée en paramètre. Elle est un peu délicate à utiliser, voyons un exemple. Considérons deux classes : Marin et Capitaine. La classe Marin implémente Serializable, et la classe Capitaine étend Marin.

Exemple 48. Utilisation de Class.isAssignableFrom(Class)

// dans un fichier Marin.java
 public  class Marin  implements Serializable {

    // contenu de la classe
}

 // dans un fichier Capitaine.java
 public  class Capitaine  extends Marin {

    // contenu de la classe
}

 // dans une méthode main()
 boolean b1 = Capitaine.class.isAssignableFrom(Marin.class) ;         // b1 est false
 boolean b2 = Marin.class.isAssignableFrom(Capitaine.class) ;         // b2 est true
 boolean b3 = Capitaine.class.isAssignableFrom(Serializable.class) ;  // b3 est false
 boolean b4 = Serializable.class.isAssignableFrom(Capitaine.class) ;  // b4 est true

2.6. Cas des énumérations

Dans le cas où la classe que l'on manipule est une énumération, on peut obtenir le tableau des constantes définies dans cette énumération. Voyons ceci sur un exemple.

Exemple 49. Introspection d'une énumération : définition de l' enum

public enum Grade {

   MOUSSE, CRABE, CHOUFFE, BOSCO, PACHA
}

Interrogeons maintenant cette classe pour découvrir ses constantes.

Exemple 50. Introspection d'une énumération : découvertes des valeurs constantes

public  static  void main(String[] args) {

   Grade[] grades = Grade.class.getEnumConstants() ;
   System.out.println(Arrays.toString(grades)) ;
}

L'exécution de ce code affiche le résultat suivant.
 [MOUSSE, CRABE, CHOUFFE, BOSCO, PACHA]
Java API avancées
Retour au blog Java le soir
Cours & Tutoriaux
Table des matières
API Collection
1. Introduction
2. Interface Collection
2.1. Notion de Collection
2.2. Détail des méthodes disponibles
2.3. Interface Iterator
2.4. Implémentation, exemples d'utilisation
3. Interface List
3.1. Notion de List
3.2. Détail des méthodes disponibles
3.3. Interface ListIterator
3.4. Implémentations, exemples d'utilisation
4. Interface Set
4.1. Notion de Set
4.2. Implémentations HashSet et LinkedHashSet
4.3. Exemples d'utilisation
5. Interface SortedSet
5.1. Notion de SortedSet
5.2. Détails des méthodes disponibles
5.3. Exemples d'utilisation
6. Interface NavigableSet
6.1. Notion de NavigableSet
6.2. Détails des méthodes disponibles
6.3. Exemple d'utilisation
7. Interfaces Queue et Deque
7.1. Notion de file d'attente
7.2. Détail des méthodes disponibles
7.3. Utilisation des interfaces Queue et Deque
8. Tables de hachage
8.1. Notion de table de hachage
8.2. Interface Map
8.3. Interface Map.Entry
8.4. Interface SortedMap
8.5. Interface NavigableMap
8.6. Implémentations
8.7. Exemples d'utilisation
9. Classes utilitaires Collections et Arrays
9.1. Introduction
9.2. Classe Arrays
9.3. Classe Collections
Génériques
1. Introduction
2. Un premier exemple
2.1. Une première classe générique
2.2. Une première méthode générique
3. Contraindre un type générique
3.1. Problème posé
3.2. Contraindre un type générique
4. Implémentation des génériques
4.1. Type erasure
4.2. Types génériques et casts
4.3. Type générique et exception
4.4. Construction d'une instance générique
4.5. Génériques et membres statiques
4.6. Collisions de méthodes génériques
4.7. Implémentation de plusieurs types identiques
5. Type <?>
5.1. Introduction
5.2. Type ? extension d'un type
5.3. Type ? super-type d'un type
Expressions régulières
1. Introduction
2. Mise en œuvre des expressions régulières
2.1. Fonctionnement d'une regexp
2.2. Fonctionnement de l'API en Java
2.3. Un premier exemple
2.4. Classe Pattern
2.5. Classe Matcher
2.6. Utilisation des méthode find() et group()
2.7. Méthodes de remplacement
2.8. Sélection de régions
3. Syntaxe des expressions régulières
3.1. Notion de classe
3.2. Étude d'un cas réel
3.3. Recherche d'un mot précis
3.4. Recherche de deux mots précis
3.5. Recherche d'un mot commençant par une lettre donnée
3.6. Cas de mots comportant des caractères accentués
3.7. Recherche sur les lignes
Introspection
1. Introduction
2. La classe Class
2.1. Utilisation de Class
2.2. Méthodes disponibles
2.3. Remarque sur la propriété Accessible
2.4. Type d'une classe
2.5. Création d'une instance à partir d'un objet Class
2.6. Cas des énumérations
3. Les classes Method et Constructor
3.1. Utilisation de Method
3.2. Utilisation de Constructor
3.3. Méthodes disponibles
3.4. Invocation d'une méthode par introspection
4. La classe Field
4.1. Utilisation de Field
4.2. Méthodes disponibles
4.3. Accès à un champ par introspection
5. La classe Modifier
Programmation concurrente
1. Introduction
2. Lançons nos premiers threads
2.1. Introduction
2.2. Un premier thread, extension de Thread
2.3. Un deuxième thread, implémentation de Runnable
2.4. Remarque sur la méthode Thread.sleep(long)
2.5. Arrêter un thread
3. Concurrence d'accès
3.1. Notion d'état
3.2. Exemple de concurrence d'accès sur un état
3.3. Analyse de la concurrence d'accès
3.4. Solution au problème
3.5. Champs volatile
4. Synchronisation
4.1. Définition d'un bloc synchronisé
4.2. Fonctionnement d'un bloc synchronisé
4.3. Notion de deadlock
4.4. Bonnes pratiques pour la synchronisation de threads
5. Opérations atomiques
5.1. Atomicité d'une opération
5.2. Solutions disponibles
5.3. Variables atomiques
6. Collections synchronisées et concurrentes
6.1. Introduction
6.2. Position du problème
6.3. Solutions proposées
7. Files d'attente
7.1. Introduction, pattern producteur / consommateur
7.2. Interface BlockingQueue<E>
7.3. Implémentations de BlockingQueue
7.4. Exemple de producteur / consommateur
7.5. Arrêter un producteur / consommateur : pilule empoisonnée
8. Classes utilitaires de l'API Concurrent
8.1. Introduction
8.2. Énumération TimeUnit
8.3. Interface Callable<V>
8.4. Interfaces Future<V> et RunnableFuture<V>
8.5. Interface ScheduledFuture<V> et RunnableScheduledFuture<V>
9. Pattern executor
9.1. Notion de réserve de threads
9.2. Interface Executor
9.3. Interface ExecutorService
9.4. Interface ScheduledExecutorService
9.5. Classe Executors
9.6. Pattern de lancement de tâches
10. Classes de contrôle d'accès
10.1. Introduction
10.2. Interfaces Lock et ReadWriteLock
10.3. Notion de verrou réentrant
10.4. Classe RentrantLock
10.5. Classe ReadWriteRentrantLock
11. Sémaphores, barrières et latches
11.1. Introduction
11.2. Notion de sémaphore, classe Semaphore
11.3. Notion de latch, classe CountDownLatch
11.4. Notion de barrière, classe CyclicBarrier