Cet article décrit en détail le mécanisme de réflexion de Java sous forme d'exemples, ce qui constitue une compétence importante en programmation Java. Partagez-le avec tout le monde pour votre référence. L’analyse spécifique est la suivante :
Tout d'abord, Reflection est l'une des fonctionnalités du langage de développement de programmes Java. Il permet au programme Java en cours d'exécution de s'auto-vérifier, ou « d'auto-audit », et d'exploiter directement les propriétés internes du programme . Par exemple, utilisez-le pour obtenir le nom de chaque membre d'une classe Java et l'afficher. Cette capacité de Java n'est peut-être pas beaucoup utilisée dans les applications pratiques, mais cette fonctionnalité n'existe pas du tout dans d'autres langages de programmation. Par exemple, en Pascal, C ou C++, il n'existe aucun moyen d'obtenir des informations sur les définitions de fonctions dans le programme.
JavaBean est l'une des applications pratiques de réflexion, qui permet à certains outils de faire fonctionner visuellement des composants logiciels. Ces outils chargent et obtiennent dynamiquement les propriétés des composants Java (classes) par réflexion.
1. Un exemple simple
Considérez l'exemple simple suivant et voyons comment fonctionne la réflexion.
importer java.lang.reflect.*; public class DumpMethods { public static void main(String args[]) { try { Class c = Class.forName("java.util.Stack"); Méthode m[] = c.getDeclaredMethods (); for (int i = 0; i < m.length; i++) System.out.println(m[i].toString() ); e){ Système.err.println(e); } } }
Son résultat est :
public synchronisé java.lang.Object java.util.Stack.pop()public java.lang.Object java.util.Stack.push(java.lang.Object)public booléen java.util.Stack.empty()public synchronisé java .lang.Object java.util.Stack.peek() public synchronisé int java.util.Stack.search(java.lang.Object)
Ceci répertorie les noms de méthodes de la classe java.util.Stack ainsi que leurs qualificatifs et types de retour.
Ce programme utilise Class.forName pour charger la classe spécifiée, puis appelle getDeclaredMethods pour obtenir la liste des méthodes définies dans la classe. java.lang.reflect.Methods est une classe utilisée pour décrire une seule méthode dans une classe.
2. Commencez à utiliser Reflection
Les classes de réflexion, telles que Method, peuvent être trouvées dans le package java.lang.relfect. Il y a trois étapes que vous devez suivre lorsque vous utilisez ces classes : La première étape consiste à obtenir l'objet java.lang.Class de la classe sur laquelle vous souhaitez opérer. Dans un programme Java en cours d'exécution, la classe java.lang.Class est utilisée pour décrire les classes, les interfaces, etc.
Voici l'une des façons d'obtenir un objet Class :
Classe c = Class.forName("java.lang.String");
Cette instruction obtient un objet de classe de la classe String. Il existe une autre manière, comme la déclaration suivante :
Classe c = int.class ; ou Classe c = Integer.TYPE ;
Ils obtiennent des informations de classe pour les types de base. Cette dernière méthode accède au champ TYPE prédéfini dans la classe d'encapsulation de type de base (telle que Integer).
La deuxième étape consiste à appeler une méthode telle que getDeclaredMethods pour obtenir une liste de toutes les méthodes définies dans la classe.
Une fois ces informations obtenues, vous pouvez passer à la troisième étape : utiliser l'API de réflexion pour exploiter ces informations, comme le code suivant :
Classe c = Class.forName("java.lang.String"); Méthode m[] = c.getDeclaredMethods(); System.out.println(m[0].toString());
Il imprimera textuellement le prototype de la première méthode définie dans String.
Dans l'exemple suivant, ces trois étapes illustrent l'utilisation de la réflexion pour une application particulière.
Simuler l'opérateur instanceof
Après avoir obtenu les informations sur la classe, l'étape suivante consiste généralement à résoudre quelques questions de base sur l'objet Class. Par exemple, la méthode Class.isInstance peut être utilisée pour simuler l'opérateur instanceof :
class S { } public class IsInstance { public static void main(String args[]) { try { Class cls = Class.forName("S"); .println(b1); booléen b2 = cls.isInstance(new S()); System.out.println(b2); (Jetable e) { System.err.println(e);
Dans cet exemple, un objet Class de classe S est créé, puis certains objets sont vérifiés pour voir s'ils sont des instances de S. Integer(37) ne l'est pas, mais new S() l'est.
3. Trouver la méthode de la classe
Découvrir quelles méthodes sont définies dans une classe est une utilisation de réflexion très précieuse et basique. Le code suivant implémente cette utilisation :
import java.lang.reflect.*; public class Method1 { private int f1(Object p, int x) throws NullPointerException { if (p == null) throw new NullPointerException(); public static void main(String args); []) { try { Class cls = Class.forName("Method1"); Méthode methlist[] = cls.getDeclaredMethods(); for (int i = 0; i < methlist.length; i++) { Méthode m = methlist[i]; System.out.println("name = " + m.getName()); class = " + m.getDeclaringClass()); Class pvec[] = m.getParameterTypes(); for (int j = 0; j < pvec.length; j++) System.out.println("param #" + j + " " + pvec[j]); Classe evec[] = m.getExceptionTypes(); for (int j = 0; j < evec.length; j++) Système. out.println("exc #" + j + " " + evec[j]); System.out.println("return type = " + m.getReturnType()); System.out.println("-----"); } } catch (Jetable e) { System.err.println(e } } }
Ce programme obtient d'abord la description de la classe method1, puis appelle getDeclaredMethods pour obtenir une série d'objets Method, qui décrivent chaque méthode définie dans la classe, y compris les méthodes publiques, les méthodes protégées, les méthodes de package et les méthodes privées. Si vous utilisez getMethods au lieu de getDeclaredMethods dans votre programme, vous pouvez également obtenir des informations sur chaque méthode héritée.
Après avoir obtenu la liste d'objets Méthode, il n'est pas difficile d'afficher les types de paramètres, les types d'exceptions, les types de valeurs de retour, etc. Que ces types soient des types primitifs ou des types de classe, ils peuvent être donnés dans l'ordre par les objets décrivant la classe.
Les résultats de sortie sont les suivants :
nom = f1 decl classe = classe méthode1 param #0 classe java.lang.Object param #1 int exc #0 classe java.lang.NullPointerException return type = int-----name = main decl classe = classe méthode1 param #0 classe [Ljava.lang.String; type de retour = void
4. Obtenez des informations sur le constructeur
L'utilisation de l'obtention du constructeur de classe est similaire à l'utilisation de la méthode d'obtention ci-dessus, telle que :
import java.lang.reflect.*;public class Constructor1 { public Constructor1() { } protected Constructor1(int i, double d) { } public static void main(String args[]) { try { Class cls = Class.forName( "Constructor1"); Constructeur ctorlist[] = cls.getDeclaredConstructors(); for (int i = 0; i < ctorlist.length; i++) { Constructeur ct = ctorlist[i]; System.out.println("name = " + ct.getName()); System.out.println("decl class = " + ct.getDeclaringClass()); [] = ct.getParameterTypes(); for (int j = 0; j < pvec.length; j++) System.out.println("param #" + j + " " + pvec[j]); Classe evec[] = ct.getExceptionTypes(); for (int j = 0; j < evec.length; j++) System.out.println("exc #" + j + " " + evec[j]); System.out.println("-----"); } } catch (Jetable e) { System.err.println(e } } }
Dans cet exemple, aucune information sur le type de retour ne peut être obtenue, car le constructeur n'a pas de type de retour.
Le résultat de l'exécution de ce programme est :
nom = Constructeur1decl classe = classe Constructeur1param #0 intparam #1 double-----nom = Constructeur1decl classe = classe Constructeur1-----
5. Récupérez les champs (domaines) de la classe
Il est également possible de savoir quels champs de données sont définis dans une classe, comme le fait le code suivant :
import java.lang.reflect.*; public class Field1 { private double d; public static final int i = 37; String s = "test"; public static void main(String args[]) { try { Classe cls = Classe. forName("Field1"); Champ fieldlist[] = cls.getDeclaredFields(); for (int i = 0; i < fieldlist.length; i++) { Field fld = fieldlist[i]; System.out.println("name = " + fld.getName()); System.out.println("decl class = " + fld.getDeclaringClass()); = " + fld.getType()); int mod = fld.getModifiers(); System.out.println("modifiers = " + Modifier.toString(mod)); System.out.println("-----"); } } catch (Jetable e) { System.err.println(e } } }
Cet exemple est très similaire au précédent. Dans cet exemple, un nouveau modificateur d'objet est utilisé, qui est également une classe de réflexion et est utilisé pour décrire les modificateurs des membres du champ, tels que "private int". Les modificateurs eux-mêmes sont décrits par des entiers, et Modifier.toString est utilisé pour renvoyer les descriptions de chaînes dans l'ordre « officiel » (par exemple « statique » avant « final »). Le résultat de ce programme est :
nom = ddecl classe = classe Field1type = doublemodifiers = privé -----nom = idecl classe = classe Field1type = intmodifiers = public static final -----name = sdecl classe = classe Field1type = classe java.lang.Stringmodifiers = - ----
Comme pour la méthode get, lors de l'obtention de champs, vous pouvez également obtenir uniquement les informations de champ déclarées dans la classe actuelle (getDeclaredFields), ou vous pouvez également obtenir les champs définis dans la classe parent (getFields).
6. Exécutez la méthode selon son nom
À ce stade du texte, les exemples donnés sont tous liés à la manière d’obtenir des informations sur la classe. Nous pouvons également utiliser la réflexion pour faire d'autres choses, comme exécuter une méthode avec un nom spécifié. L'exemple suivant illustre cette opération :
importer java.lang.reflect.*; public class Method2 { public int add(int a, int b) { return a + b; } public static void main(String args[]) { try { Class cls = Class.forName( "Méthode2"); Classe partypes[] = new Class[2]; partypes[0] = Integer.TYPE[1] = Integer.TYPE ; cls.getMethod("add", partypes); Method2 methobj = new Method2(); Object arglist[] = new Object[2]; arglist[0] = new Integer(37]); ); Objet retobj = meth.invoke(methobj, arglist); Integer retval = (Integer) retobj; System.out.println(retval.intValue()); } catch (Throwable e) { System.err.println(e } } }
Si un programme sait seulement qu'une certaine méthode doit être exécutée quelque part pendant l'exécution et que le nom de cette méthode est spécifié lors de l'exécution du programme (par exemple, cela est fait dans l'environnement de développement JavaBean), alors le programme ci-dessus est démontré comment le faire.
Dans l'exemple ci-dessus, getMethod est utilisé pour rechercher une méthode nommée add qui a deux paramètres entiers. Après avoir trouvé la méthode et créé l’objet Method correspondant, exécutez-le dans l’instance d’objet appropriée. Lors de l'exécution de cette méthode, vous devez fournir une liste de paramètres, qui dans l'exemple ci-dessus sont deux objets Integer qui enveloppent respectivement les entiers 37 et 47. La méthode d'exécution renvoie un objet Integer, qui encapsule la valeur de retour 84.
7. Créez de nouveaux objets
Pour les constructeurs, vous ne pouvez pas procéder comme exécuter des méthodes, car exécuter un constructeur signifie créer un nouvel objet (pour être précis, le processus de création d'un objet inclut l'allocation de mémoire et la construction de l'objet). Ainsi, l’exemple le plus similaire à l’exemple ci-dessus est le suivant :
import java.lang.reflect.*; public class Constructor2 { public Constructor2() { } public Constructor2(int a, int b) { System.out.println("a = " + a + " b = " + b); } public static void main(String args[]) { try { Class cls = Class.forName("Constructor2"); Class partypes[] = new Class[2]; partypes[0] = Integer.TYPE; partypes[1] = Integer.TYPE; Constructeur ct = cls.getConstructor(partypes); Object arglist[] = new Object[2]; arglist[1] = new Integer(47); Objet retobj = ct.newInstance(arglist } catch); (Jetable e) { System.err.println(e);
Recherchez le constructeur correspondant en fonction des types de paramètres spécifiés et exécutez-le pour créer une nouvelle instance d'objet. L'utilisation de cette approche vous permet de créer dynamiquement des objets pendant l'exécution du programme plutôt qu'au moment de la compilation, ce qui est très utile.
8. Changer la valeur d'un champ (domaine)
Une autre utilisation de la réflexion consiste à modifier les valeurs des champs de données des objets. Reflection peut rechercher les champs d'un objet par leur nom à partir d'un programme en cours d'exécution et les modifier. L'exemple suivant illustre cela :
importer java.lang.reflect.*; public class Field2 { public double d; public static void main(String args[]) { try { Class cls = Class.forName("Field2"); d"); Field2 f2obj = new Field2(); System.out.println("d = " + f2obj.d); fld.setDouble(f2obj, 12.34); System.out.println("d = " + f2obj.d } catch (Throwable e) { System.err.println(e);
Dans cet exemple, la valeur du champ d est modifiée à 12,34.
9. Utilisez des tableaux
L'utilisation finale de la réflexion présentée dans cet article est la création de tableaux d'opérandes. Array est un type de classe spécial dans le langage Java et une référence de tableau peut être attribuée à une référence d'objet. Regardez l'exemple suivant pour voir comment fonctionnent les tableaux :
importer java.lang.reflect.*; public class Array1 { public static void main(String args[]) { try { Class cls = Class.forName("java.lang.String"); Object arr = Array.newInstance(cls) , 10); Array.set(arr, 5, "ceci est un test"); String s = (String) Array.get(arr, 5); System.out.println(s); } catch (Jetable e) { System.err.println(e } } }
Dans l'exemple, un tableau de chaînes de 10 unités de longueur est créé, une valeur est attribuée à la chaîne en 5ème position et enfin la chaîne est obtenue à partir du tableau et imprimée.
Le code suivant fournit un exemple plus complexe :
importer java.lang.reflect.*; public class Array2 { public static void main(String args[]) { int dims[] = new int[]{5, 10, 15}; TYPE, dims); Objet arrobj = Array.get(arr, 3); arrobj.getClass().getComponentType(); System.out.println(cls); arrobj = Array.get(arrobj, 5); Array.setInt(arrobj, 10, 37); (int[][][]) arr; System.out.println(arrcast[3][5][10]);
Dans l'exemple, un tableau d'entiers 5 x 10 x 15 est créé et l'élément en [3][5][10] reçoit la valeur 37. Notez que les tableaux multidimensionnels sont en fait des tableaux de tableaux, par exemple, après le premier Array.get, arrobj est un tableau 10 x 15. Ensuite, récupérez l'un des éléments, c'est-à-dire un tableau d'une longueur de 15, et utilisez Array.setInt pour attribuer une valeur à son 10ème élément.
Notez que le type du tableau lors de sa création est dynamique et que son type n'est pas connu au moment de la compilation .
Je pense que ce que décrit cet article a une certaine valeur de référence pour l'apprentissage de la programmation Java par chacun.