![translation](https://cdn.durumis.com/common/trans.png)
Ceci est un post traduit par IA.
[Java] Concept de réflexion et méthodes d'utilisation
- Langue de rédaction : Coréen
- •
-
Pays de référence : Tous les pays
- •
- Technologies de l'information
Choisir la langue
Texte résumé par l'IA durumis
- La réflexion est une API qui permet d'accéder aux informations de classe au moment de l'exécution et de manipuler les classes comme vous le souhaitez.
- La réflexion vous permet de créer des instances de classe et d'accéder aux champs et aux méthodes indépendamment des modificateurs d'accès. Elle est particulièrement utile pour gérer dynamiquement les dépendances dans les phases de développement à grande échelle, comme avec les frameworks.
- Cependant, il est préférable de l'utiliser uniquement lorsque cela est nécessaire, car il peut violer l'encapsulation et entraîner des problèmes de performances.
Qu'est-ce que la réflexion ?
La réflexion est une API qui permet de créer des instances de classe souhaitées via des objets de type Class chargés dans la zone de tas et d'accéder aux champs et aux méthodes de l'instance indépendamment des modificateurs d'accès.
Ici, le terme "classes chargées" signifie que le chargeur de classes JVM a terminé le chargement du fichier de classe et a créé unobjet de type Classcontenant les informations de la classe, qui sont stockées dans la zone de tas en mémoire. Il faut noter que ce n'est pas la même chose que les objets créés à l'aide du mot-clé new. Si vous ne comprenez pas bien les objets de type Class, vous pouvez consulter la documentation JDK de java.lang.class.
Comment l'utiliser
Avant d'utiliser la réflexion, vous devez obtenir l'objet de type Class qui est chargé dans la zone de tas. Il existe trois méthodes pour ce faire.
- Obtenez-le via Classe.class
- Obtenez-le via Instance.getClass()
- Obtenez-le via Class.forName("Nom de la classe")
public class Member {
private String name;
protected int age;
public String hobby;
public Member() {
}
public Member(String name, int age, String hobby) {
this.name = name;
this.age = age;
this.hobby = hobby;
}
public void speak(String message) {
System.out.println(message);
}
private void secret() {
System.out.println("Le mot de passe est 1234.");
}
@Override
public String toString() {
return "Member{" +
"name='" + name + '\'' +
", age=" + age +
", hobby='" + hobby + '\'' +
'}';
}
}
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
Class memberClass = Member.class;
System.out.println(System.identityHashCode(memberClass));
Member member = new Member("Jayeon", 23, "Développement de Darath");
Class extends Member> memberClass2 = member.getClass();
System.out.println(System.identityHashCode(memberClass2));
Class> memberClass3 = Class.forName("{Nom du paquet}.Member");
System.out.println(System.identityHashCode(memberClass3));
}
}
// Résultat de l'exécution
1740000325
1740000325
Vous pouvez voir que les trois instances de type Class obtenues par ces trois méthodes sont les mêmes. Quelle que soit la méthode utilisée, la valeur de hachage est la même, vous pouvez donc les utiliser en fonction des circonstances.
Maintenant, à partir de l'instance de type Class obtenue, vous pouvez créer des instances de cette classe et accéder aux champs et aux méthodes de l'instance indépendamment des modificateurs d'accès. Commençons par créer des instances de cette classe.
public class Main {
public static void main(String[] args) throws Exception {
// Affichage de tous les constructeurs de Member
Member member = new Member();
Class extends Member> memberClass = member.getClass();
Arrays.stream(memberClass.getConstructors()).forEach(System.out::println);
// Création d'une instance via le constructeur par défaut de Member
Constructor extends Member> constructor = memberClass.getConstructor();
Member member2 = constructor.newInstance();
System.out.println("member2 = " + member2);
// Création d'une instance via un autre constructeur de Member
Constructor extends Member> fullConstructor =
memberClass.getConstructor(String.class, int.class, String.class);
Member member3 = fullConstructor.newInstance("Jayeon", 23, "Développement de Darath");
System.out.println("member3 = " + member3);
}
}
// Résultat de l'exécution
public Member()
public Member(java.lang.String,int,java.lang.String)
member2 = Member{name='null', age=0, hobby='null'}
getConstructor() vous permet d'obtenir un constructeur et newInstance() vous permet de créer dynamiquement une instance de Member.
Enfin, accédons aux champs et aux méthodes de l'instance indépendamment des modificateurs d'accès.
public class Main {
public static void main(String[] args) throws Exception {
Member member = new Member("Jayeon", 23, "Développement de Darath");
Class extends Member> memberClass = member.getClass();
// Accès au champ
Field[] fields = memberClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.get(member));
}
fields[0].set(member, "Jayeon2");
System.out.println(member);
// Accès à la méthode
Method speakMethod = memberClass.getDeclaredMethod("speak", String.class);
speakMethod.invoke(member, "Test de réflexion");
Method secretMethod = memberClass.getDeclaredMethod("secret");
secretMethod.setAccessible(true);
secretMethod.invoke(member);
}
}
// Résultat de l'exécution
Jayeon
23
Développement de Darath
Member{name='Jayeon2', age=23, hobby='Développement de Darath'}
Test de réflexion
getDeclaredFileds() vous permet d'obtenir toutes les variables d'instance de la classe, get() vous permet d'obtenir la valeur du champ et set() vous permet de modifier la valeur du champ. Il faut noter qu'il faut passer true comme argument de setAccessible() pour accéder à un champ avec un modificateur d'accès private.
De même, getDeclaredMethod() vous permet d'obtenir une méthode. Vous devez passer le nom de la méthode et le type de paramètre comme arguments. De même, vous devez définir l'argument de setAccessible() sur true pour accéder à une méthode avec un modificateur d'accès private. Enfin, vous pouvez appeler la méthode obtenue via l'API de réflexion à l'aide de la méthode invoke().
Avantages et inconvénients
- Avantages
- L'API de réflexion offre une flexibilité, car elle permet de créer des instances de classe au moment de l'exécution et d'accéder aux champs et aux méthodes indépendamment des modificateurs d'accès, afin d'effectuer les tâches nécessaires.
- Inconvénients
- Elle viole l'encapsulation.
- Les instances étant créées au moment de l'exécution, le type ne peut pas être vérifié au moment de la compilation.
- Les instances étant créées au moment de l'exécution, il est difficile de comprendre le flux d'exécution précis.
- L'accès via la réflexion est plus lent que l'accès direct aux champs et aux méthodes (mais ce n'est pas toujours le cas).
Raisons d'utilisation
L'API de réflexion vous permet d'accéder aux informations de classe au moment de l'exécution et de manipuler la classe à votre guise. Vous pouvez même manipuler les champs et les méthodes déclarés avec le modificateur d'accès private. Cela semble être une technique à éviter, car elle viole l'encapsulation, qui est un principe important de la conception orientée objet.
Dans un environnement de console de petite taille, le développeur peut facilement comprendre tous les objets et les dépendances utilisés dans le programme au moment de la compilation. Mais dans le cadre d'un développement de grande envergure comme les frameworks, il est difficile de comprendre un grand nombre d'objets et de dépendances. L'utilisation de la réflexion permet de créer dynamiquement des classes et d'établir des dépendances.
Par exemple, la fabrique de beans de Spring crée et gère les classes annotées avec @Controller, @Service, @Repository, etc., en attachant simplement ces annotations. Le développeur n'a pas informé la fabrique de beans de ces classes, mais cela est possible grâce à la réflexion. Si elle trouve une classe annotée au moment de l'exécution, elle utilise la réflexion pour créer une instance de cette classe, injecter les champs nécessaires et la stocker dans la fabrique de beans.
Bien sûr, il faut éviter de l'utiliser à moins que ce ne soit absolument nécessaire, car elle viole l'encapsulation, comme indiqué ci-dessus.