Maison  >  Article  >  Java  >  Explication détaillée du mécanisme de réflexion Java

Explication détaillée du mécanisme de réflexion Java

巴扎黑
巴扎黑original
2017-06-23 16:38:481217parcourir

1. Concept

La réflexion consiste à mapper divers composants de Java dans les classes Java correspondantes.

La méthode de construction de la classe Class est privée et est créée par la JVM.

La réflexion est une fonctionnalité du langage Java, qui permet au programme d'effectuer des auto-vérifications et de faire fonctionner les membres internes pendant l'exécution (notez qu'il n'est pas compilé). Par exemple, il permet à une classe Java d’obtenir toutes ses variables et méthodes membres et de les 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 un programme. (From Sun)

JavaBean est l'une des applications pratiques de la 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.

La réflexion existe depuis la 1.2. Les trois prochains frameworks majeurs utiliseront tous le mécanisme de réflexion. En ce qui concerne la classe "Class", vous ne pouvez pas directement new CLass(). mémoire. 

 Les instances de la classe Class représentent des classes et des interfaces dans une application Java en cours d'exécution. Une énumération est une classe et une annotation est une interface. Chaque tableau appartient à une classe mappée à un objet Class, qui est partagé par tous les tableaux ayant le même type d'élément et la même dimension. Les types Java de base (boolean, byte, char, short, int, long, float et double) et le mot-clé void sont également représentés sous forme d'objets Class. La classe n'a pas de constructeur public. Les objets de classe sont automatiquement construits par la machine virtuelle Java lorsqu'une classe est chargée et en appelant la méthode DefinClass dans le chargeur de classe.

1 Person p1 = new Person();
2 //下面的这三种方式都可以得到字节码
3 CLass c1 = Date.class();
4 p1.getClass(); 
5 //若存在则加载,否则新建,往往使用第三种,类的名字在写源程序时不需要知道,到运行时再传递过来
6 Class.forName("java.lang.String");

Le bytecode CLass.forName() a été chargé dans la machine virtuelle Java pour obtenir le bytecode ; le bytecode n'a pas été généré dans la machine virtuelle Java et est chargé avec des classes ; Le chargeur est chargé et le bytecode chargé est mis en mémoire tampon dans la machine virtuelle.

Considérez l'exemple simple suivant pour voir comment fonctionne la réflexion.

import java.lang.reflect.*;  

public class DumpMethods {  
   public static void main(String args[]) {  
      try {  
           Class c = Class.forName("java.util.Stack");  

           Method m[] = c.getDeclaredMethods();  
             
           for (int i = 0; i < m.length; i++)  
               System.out.println(m[i].toString());  
      }  
      catch (Throwable e){  
            System.err.println(e);  
      }  
   }  
}

1 public synchronized java.lang.Object java.util.Stack.pop() 
2 public java.lang.Object java.util.Stack.push(java.lang.Object) 
3 public boolean java.util.Stack.empty() 
4 public synchronized java.lang.Object java.util.Stack.peek() 
5 public synchronized int java.util.Stack.search(java.lang.Object)

 

Ceci répertorie les méthodes des noms de classe java.util.Stack et leurs qualificatifs et types de retour. Ce programme charge la classe spécifiée à l'aide de Class.forName, 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.

L'exemple suivant utilise l'objet Class pour afficher le nom de classe de l'objet :

1 void printClassName(Object obj) {
2          System.out.println("The class of " + obj +
3                             " is " + obj.getClass().getName());
4      }

Vous pouvez également utiliser un littéral de classe (JLS Section 15.8.2 ) pour obtenir l'objet Class spécifié de type (ou void). Par exemple :

1 System.out.println("The name of class Foo is: "+Foo.class.getName());

Lorsqu'il n'y a pas d'instance d'objet, il existe deux méthodes principales.

//获得类类型的两种方式        
Class cls1 = Role.class;        
Class cls2 = Class.forName("yui.Role");

Notez que dans la deuxième méthode, le paramètre dans forName doit être le nom de classe complet (nom du package + nom de la classe), et cette méthode doit intercepter les exceptions. Maintenant que vous disposez de cls1, vous pouvez créer une instance de la classe Role. Utiliser la méthode newInstance de Class équivaut à appeler le constructeur par défaut de la classe.

1 Object o = cls1.newInstance(); 
2 //创建一个实例        
3 //Object o1 = new Role();   //与上面的方法等价

2. Méthodes couramment utilisées

1. isPrimitive (déterminer s'il s'agit d'un type de bytecode de base)

public class TestReflect {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String str = "abc";
        Class cls1 = str.getClass();
        Class cls2 = String.class;
        Class cls3 = null;//必须要加上null
        try {
            cls3 = Class.forName("java.lang.String");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(cls1==cls2);
        System.out.println(cls1==cls3);
        
        System.out.println(cls1.isPrimitive());
        System.out.println(int.class.isPrimitive());//判定指定的 Class 对象是否表示一个基本类型。
        System.out.println(int.class == Integer.class);
        System.out.println(int.class == Integer.TYPE);
        System.out.println(int[].class.isPrimitive());
        System.out.println(int[].class.isArray());
    }
}
/*
 * true
true
false
true
false
true
false
true

 */
*/

2.getConstructor et getConstructors()

Il n'y a pas d'ordre des méthodes de construction en Java, et elles se distinguent par le type et le nombre de paramètres.

1 public class TestReflect {
2     public static void main(String[] args) throws SecurityException, NoSuchMethodException {
3         // TODO Auto-generated method stub
4         String str = "abc";
5         
6         System.out.println(String.class.getConstructor(StringBuffer.class));
7     }
8 }

3. La classe Field représente une variable membre dans une certaine classe.

 1 import java.lang.reflect.Field;
 2 public class TestReflect {
 3     public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception {
 4         ReflectPointer rp1 = new ReflectPointer(3,4);
 5         Field fieldx = rp1.getClass().getField("x");//必须是x或者y
 6         System.out.println(fieldx.get(rp1));
 7         
 8         /*
 9          * private的成员变量必须使用getDeclaredField,并setAccessible(true),否则看得到拿不到
10          */
11         Field fieldy = rp1.getClass().getDeclaredField("y");
12         fieldy.setAccessible(true);//暴力反射
13         System.out.println(fieldy.get(rp1));
14         
15     }
16 }
17 
18 class ReflectPointer {
19     
20     public int x = 0;
21     private int y = 0;
22     
23     public ReflectPointer(int x,int y) {//alt + shift +s相当于右键source
24         super();
25         // TODO Auto-generated constructor stub
26         this.x = x;
27         this.y = y;
28     }
29 }

 4.

3. Exemples typiques

 1. Remplacez b dans toutes les variables membres de type String par a.

 1 import java.lang.reflect.Field;
 2 public class TestReflect {
 3     public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception {
 4         ReflectPointer rp1 = new ReflectPointer(3,4);
 5         changeBtoA(rp1);
 6         System.out.println(rp1);
 7         
 8     }
 9     
10     private static void changeBtoA(Object obj) throws RuntimeException, Exception {
11         Field[] fields = obj.getClass().getFields();
12         
13         for(Field field : fields) {
14             //if(field.getType().equals(String.class))
15             //由于字节码只有一份,用equals语义不准确
16             if(field.getType()==String.class) {
17                 String oldValue = (String)field.get(obj);
18                 String newValue = oldValue.replace('b', 'a');
19                 field.set(obj,newValue);
20             }
21         }
22     }
23 }
24 
25 class ReflectPointer {
26     
27     private int x = 0;
28     public int y = 0;
29     public String str1 = "ball";
30     public String str2 = "basketball";
31     public String str3 = "itcat";
32     
33     public ReflectPointer(int x,int y) {//alt + shift +s相当于右键source
34         super();
35         // TODO Auto-generated constructor stub
36         this.x = x;
37         this.y = y;
38     }
39 
40     @Override
41     public String toString() {
42         return "ReflectPointer [str1=" + str1 + ", str2=" + str2 + ", str3="
43                 + str3 + "]";
44     }
45 }

 2. Écrivez un programme pour appeler la méthode principale de la classe en fonction du nom de classe fourni par l'utilisateur.

Pourquoi utiliser la réflexion ?

 1 import java.lang.reflect.Field;
 2 import java.lang.reflect.Method;
 3 
 4 public class TestReflect {
 5     public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception {
 6         String str = args[0];
 7         /*
 8          * 这样会数组角标越界,因为压根没有这个字符数组
 9          * 需要右键在run as-configurations-arguments里输入b.Inter(完整类名)
10          * 
11          */
12         Method m = Class.forName(str).getMethod("main",String[].class);
13         //下面这两种方式都可以,main方法需要一个参数
14         
15         m.invoke(null, new Object[]{new String[]{"111","222","333"}});
16         m.invoke(null, (Object)new String[]{"111","222","333"});//这个可以说明,数组也是Object
17         /*
18          * m.invoke(null, new String[]{"111","222","333"})
19          * 上面的不可以,因为java会自动拆包
20          */
21     }
22 }
23 
24 class Inter {
25     public static void main(String[] args) {
26         for(Object obj : args) {
27             System.out.println(obj);
28         }
29     }
30 }

 3. Simuler l'instance de l'opérateur

class S {  
}   

public class IsInstance {  
   public static void main(String args[]) {  
      try {  
           Class cls = Class.forName("S");  
           boolean b1 = cls.isInstance(new Integer(37));  
           System.out.println(b1);  
           boolean b2 = cls.isInstance(new S());  
           System.out.println(b2);  
      }  
      catch (Throwable e) {  
           System.err.println(e);  
      }  
   }  
}

 

Dans cet exemple, un objet S est créé. de la classe, puis vérifie si un objet est une instance de S. Integer(37) ne l'est pas, mais new S() l'est.

4. Classe de méthode

Représente une méthode dans une classe (pas un objet).

 1 import java.lang.reflect.Field;
 2 import java.lang.reflect.Method;
 3 /*
 4  * 人在黑板上画圆,涉及三个对象,画圆需要圆心和半径,但是是私有的,画圆的方法
 5  * 分配给人不合适。
 6  * 
 7  * 司机踩刹车,司机只是给列车发出指令,刹车的动作还需要列车去完成。
 8  * 
 9  * 面试经常考面向对象的设计,比如人关门,人只是去推门。
10  * 
11  * 这就是专家模式:谁拥有数据,谁就是专家,方法就分配给谁
12  */
13 public class TestReflect {
14     public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception {
15         String str = "shfsfs";
16         //包开头是com表示是sun内部用的,java打头的才是用户的
17         Method mtCharAt = String.class.getMethod("charAt", int.class);
18         Object ch = mtCharAt.invoke(str,1);//若第一个参数是null,则肯定是静态方法
19         System.out.println(ch);
20         
21         System.out.println(mtCharAt.invoke(str, new Object[]{2}));//1.4语法
22         
23     }
24     
25 }

5. Réflexion du tableau

La classe d'outils Array est utilisée pour terminer l'opération de réflexion du tableau.

Le même type et la même latitude ont le même bytecode.

int.class et Integer.class ne sont pas le même bytecode, Integer.TYPE, TYPE représente le bytecode de la classe de base correspondant à la classe de packaging int.class==Integer.TYPE

 1 import java.util.Arrays;
 2 
 3 /*
 4  * 从这个例子看出即便字节码相同但是对象也不一定相同,根本不是一回事
 5  * 
 6  */
 7 public class TestReflect {
 8     public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception {
 9         int[] a = new int[3];
10         int[] b = new int[]{4,5,5};//直接赋值后不可以指定长度,否则CE
11         int[][] c = new int[3][2];
12         String[] d = new String[]{"jjj","kkkk"};
13         System.out.println(a==b);//false
14         System.out.println(a.getClass()==b.getClass());//true
15         //System.out.println(a.getClass()==d.getClass());    //比较字节码a和cd也没法比
16         System.out.println(a.getClass());//输出class [I
17         System.out.println(a.getClass().getName());//输出[I,中括号表示数组,I表示整数
18         
19         System.out.println(a.getClass().getSuperclass());//输出class java.lang.Object
20         System.out.println(d.getClass().getSuperclass());//输出class java.lang.Object
21         
22         //由于父类都是Object,下面都是可以的
23         Object obj1 = a;//不可是Object[]
24         Object obj2 = b;
25         Object[] obj3 = c;//基本类型的一位数组只可以当做Object,非得还可以当做Object[]
26         Object obj4 = d;
27         
28         //注意asList处理int[]和String[]的区别
29         System.out.println(Arrays.asList(b));//1.4没有可变参数,使用的是数组,[[I@1bc4459]
30         System.out.println(Arrays.asList(d));//[jjj, kkkk]
31         
32     }
33 }

6. Conclusion
Ce qui précède est l'utilisation simple du mécanisme de réflexion. Évidemment, les amis qui ont étudié Spring doivent comprendre pourquoi nous pouvons obtenir les méthodes et les variables spécifiées via le fichier de configuration lorsque nous créons l'objet. implémenté en passant une chaîne, exactement comme ce dont vous avez besoin, nous le produirons pour vous, et nous utilisons Object, qui montre les caractéristiques dynamiques du langage Java et la dépendance est considérablement réduite.

Attention aux étudiants qui apprennent Java ! ! !
Si vous rencontrez des problèmes pendant le processus d'apprentissage ou si vous souhaitez obtenir des ressources d'apprentissage, vous êtes invités à rejoindre le groupe d'échange d'apprentissage Java : 159610322 Apprenons Java ensemble !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn